|
|
1.1 root 1: /*
2: * tkMenu.c --
3: *
4: * This module implements menus for the Tk toolkit. The menus
5: * support normal button entries, plus check buttons, radio
6: * buttons, iconic forms of all of the above, and separator
7: * entries.
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/tkMenu.c,v 1.37 92/08/24 09:24:04 ouster Exp $ SPRITE (Berkeley)";
21: #endif
22:
23: #include "tkconfig.h"
24: #include "default.h"
25: #include "tkint.h"
26:
27: /*
28: * One of the following data structures is kept for each entry of each
29: * menu managed by this file:
30: */
31:
32: typedef struct MenuEntry {
33: int type; /* Type of menu entry; see below for
34: * valid types. */
35: struct Menu *menuPtr; /* Menu with which this entry is associated. */
36: char *label; /* Main text label displayed in entry (NULL
37: * if no label). Malloc'ed. */
38: int labelLength; /* Number of non-NULL characters in label. */
39: int underline; /* Index of character to underline. */
40: Pixmap bitmap; /* Bitmap to display in menu entry, or None.
41: * If not None then label is ignored. */
42: char *accel; /* Accelerator string displayed at right
43: * of menu entry. NULL means no such
44: * accelerator. Malloc'ed. */
45: int accelLength; /* Number of non-NULL characters in
46: * accelerator. */
47:
48: /*
49: * Information related to displaying entry:
50: */
51:
52: Tk_Uid state; /* State of button for display purposes:
53: * normal, active, or disabled. */
54: int height; /* Number of pixels occupied by entry in
55: * vertical dimension. */
56: int y; /* Y-coordinate of topmost pixel in entry. */
57: int selectorDiameter; /* Size of selector display, in pixels. */
58: Tk_3DBorder border; /* Structure used to draw background for
59: * entry. NULL means use overall border
60: * for menu. */
61: Tk_3DBorder activeBorder; /* Used to draw background and border when
62: * element is active. NULL means use
63: * activeBorder from menu. */
64: XFontStruct *fontPtr; /* Text font for menu entries. NULL means
65: * use overall font for menu. */
66: GC textGC; /* GC for drawing text in entry. NULL means
67: * use overall textGC for menu. */
68: GC activeGC; /* GC for drawing text in entry when active.
69: * NULL means use overall activeGC for
70: * menu. */
71: GC disabledGC; /* Used to produce disabled effect for entry.
72: * NULL means use overall disabledGC from
73: * menu structure. See comments for
74: * disabledFg in menu structure for more
75: * information. */
76:
77: /*
78: * Information used to implement this entry's action:
79: */
80:
81: char *command; /* Command to invoke when entry is invoked.
82: * Malloc'ed. */
83: char *name; /* Name of variable (for check buttons and
84: * radio buttons) or menu (for cascade
85: * entries). Malloc'ed.*/
86: char *onValue; /* Value to store in variable when selected
87: * (only for radio and check buttons).
88: * Malloc'ed. */
89: char *offValue; /* Value to store in variable when not
90: * selected (only for check buttons).
91: * Malloc'ed. */
92:
93: /*
94: * Miscellaneous information:
95: */
96:
97: int flags; /* Various flags. See below for definitions. */
98: } MenuEntry;
99:
100: /*
101: * Flag values defined for menu entries:
102: *
103: * ENTRY_SELECTED: Non-zero means this is a radio or check
104: * button and that it should be drawn in
105: * the "selected" state.
106: * ENTRY_NEEDS_REDISPLAY: Non-zero means the entry should be redisplayed.
107: */
108:
109: #define ENTRY_SELECTED 1
110: #define ENTRY_NEEDS_REDISPLAY 4
111:
112: /*
113: * Types defined for MenuEntries:
114: */
115:
116: #define COMMAND_ENTRY 0
117: #define SEPARATOR_ENTRY 1
118: #define CHECK_BUTTON_ENTRY 2
119: #define RADIO_BUTTON_ENTRY 3
120: #define CASCADE_ENTRY 4
121:
122: /*
123: * Mask bits for above types:
124: */
125:
126: #define COMMAND_MASK TK_CONFIG_USER_BIT
127: #define SEPARATOR_MASK (TK_CONFIG_USER_BIT << 1)
128: #define CHECK_BUTTON_MASK (TK_CONFIG_USER_BIT << 2)
129: #define RADIO_BUTTON_MASK (TK_CONFIG_USER_BIT << 3)
130: #define CASCADE_MASK (TK_CONFIG_USER_BIT << 4)
131: #define ALL_MASK (COMMAND_MASK | SEPARATOR_MASK \
132: | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | CASCADE_MASK)
133:
134: /*
135: * Configuration specs for individual menu entries:
136: */
137:
138: static Tk_ConfigSpec entryConfigSpecs[] = {
139: {TK_CONFIG_BORDER, "-activebackground", (char *) NULL, (char *) NULL,
140: DEF_MENU_ENTRY_ACTIVE_BG, Tk_Offset(MenuEntry, activeBorder),
141: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
142: |TK_CONFIG_NULL_OK},
143: {TK_CONFIG_STRING, "-accelerator", (char *) NULL, (char *) NULL,
144: DEF_MENU_ENTRY_ACCELERATOR, Tk_Offset(MenuEntry, accel),
145: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
146: {TK_CONFIG_BORDER, "-background", (char *) NULL, (char *) NULL,
147: DEF_MENU_ENTRY_BG, Tk_Offset(MenuEntry, border),
148: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
149: |TK_CONFIG_NULL_OK},
150: #if defined(USE_XPM3)
151: {TK_CONFIG_PIXMAP, "-bitmap", (char *) NULL, (char *) NULL,
152: DEF_MENU_ENTRY_BITMAP, Tk_Offset(MenuEntry, bitmap),
153: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
154: |TK_CONFIG_NULL_OK},
155: #else
156: {TK_CONFIG_BITMAP, "-bitmap", (char *) NULL, (char *) NULL,
157: DEF_MENU_ENTRY_BITMAP, Tk_Offset(MenuEntry, bitmap),
158: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
159: |TK_CONFIG_NULL_OK},
160: #endif
161: {TK_CONFIG_STRING, "-command", (char *) NULL, (char *) NULL,
162: DEF_MENU_ENTRY_COMMAND, Tk_Offset(MenuEntry, command),
163: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
164: {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL,
165: DEF_MENU_ENTRY_FONT, Tk_Offset(MenuEntry, fontPtr),
166: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
167: |TK_CONFIG_NULL_OK},
168: {TK_CONFIG_STRING, "-label", (char *) NULL, (char *) NULL,
169: DEF_MENU_ENTRY_LABEL, Tk_Offset(MenuEntry, label),
170: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK},
171: {TK_CONFIG_STRING, "-menu", (char *) NULL, (char *) NULL,
172: DEF_MENU_ENTRY_MENU, Tk_Offset(MenuEntry, name), CASCADE_MASK},
173: {TK_CONFIG_STRING, "-offvalue", (char *) NULL, (char *) NULL,
174: DEF_MENU_ENTRY_OFF_VALUE, Tk_Offset(MenuEntry, offValue),
175: CHECK_BUTTON_MASK},
176: {TK_CONFIG_UID, "-state", (char *) NULL, (char *) NULL,
177: DEF_MENU_ENTRY_STATE, Tk_Offset(MenuEntry, state),
178: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
179: |TK_CONFIG_DONT_SET_DEFAULT},
180: {TK_CONFIG_STRING, "-onvalue", (char *) NULL, (char *) NULL,
181: DEF_MENU_ENTRY_ON_VALUE, Tk_Offset(MenuEntry, onValue),
182: CHECK_BUTTON_MASK},
183: {TK_CONFIG_STRING, "-value", (char *) NULL, (char *) NULL,
184: DEF_MENU_ENTRY_VALUE, Tk_Offset(MenuEntry, onValue),
185: RADIO_BUTTON_MASK},
186: {TK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL,
187: DEF_MENU_ENTRY_CHECK_VARIABLE, Tk_Offset(MenuEntry, name),
188: CHECK_BUTTON_MASK},
189: {TK_CONFIG_STRING, "-variable", (char *) NULL, (char *) NULL,
190: DEF_MENU_ENTRY_RADIO_VARIABLE, Tk_Offset(MenuEntry, name),
191: RADIO_BUTTON_MASK},
192: {TK_CONFIG_INT, "-underline", (char *) NULL, (char *) NULL,
193: DEF_MENU_ENTRY_UNDERLINE, Tk_Offset(MenuEntry, underline),
194: COMMAND_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CASCADE_MASK
195: |TK_CONFIG_DONT_SET_DEFAULT},
196: {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
197: (char *) NULL, 0, 0}
198: };
199:
200: /*
201: * A data structure of the following type is kept for each
202: * menu managed by this file:
203: */
204:
205: typedef struct Menu {
206: Tk_Window tkwin; /* Window that embodies the pane. NULL
207: * means that the window has been destroyed
208: * but the data structures haven't yet been
209: * cleaned up.*/
210: Tcl_Interp *interp; /* Interpreter associated with menu. */
211: MenuEntry **entries; /* Array of pointers to all the entries
212: * in the menu. NULL means no entries. */
213: int numEntries; /* Number of elements in entries. */
214: int active; /* Index of active entry. -1 means
215: * nothing active. */
216: Tk_Uid group; /* Used to allow event sharing between
217: * related menus and menu buttons. */
218:
219: /*
220: * Information used when displaying widget:
221: */
222:
223: Tk_3DBorder border; /* Structure used to draw 3-D
224: * border and background for menu. */
225: int borderWidth; /* Width of border around whole menu. */
226: Tk_3DBorder activeBorder; /* Used to draw background and border for
227: * active element (if any). */
228: int activeBorderWidth; /* Width of border around active element. */
229: XFontStruct *fontPtr; /* Text font for menu entries. */
230: XColor *fg; /* Foreground color for entries. */
231: GC textGC; /* GC for drawing text and other features
232: * of menu entries. */
233: XColor *disabledFg; /* Foreground color when disabled. NULL
234: * means use normalFg with a 50% stipple
235: * instead. */
236: Pixmap gray; /* Bitmap for drawing disabled entries in
237: * a stippled fashion. None means not
238: * allocated yet. */
239: GC disabledGC; /* Used to produce disabled effect. If
240: * disabledFg isn't NULL, this GC is used to
241: * draw text and icons for disabled entries.
242: * Otherwise text and icons are drawn with
243: * normalGC and this GC is used to stipple
244: * background across them. */
245: XColor *activeFg; /* Foreground color for active entry. */
246: GC activeGC; /* GC for drawing active entry. */
247: XColor *selectorFg; /* Color for selectors in radio and check
248: * button entries. */
249: GC selectorGC; /* For drawing selectors. */
250: int selectorSpace; /* Number of pixels to allow for displaying
251: * selectors in menu entries (includes extra
252: * space around selector). */
253: int labelWidth; /* Number of pixels to allow for displaying
254: * labels in menu entries. */
255:
256: /*
257: * Miscellaneous information:
258: */
259:
260: Cursor cursor; /* Current cursor for window, or None. */
261: MenuEntry *postedCascade; /* Points to menu entry for cascaded
262: * submenu that is currently posted, or
263: * NULL if no submenu posted. */
264: int flags; /* Various flags; see below for
265: * definitions. */
266: } Menu;
267:
268: /*
269: * Flag bits for menus:
270: *
271: * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
272: * has already been queued to redraw
273: * this window.
274: * RESIZE_PENDING: Non-zero means a call to ComputeMenuGeometry
275: * has already been scheduled.
276: */
277:
278: #define REDRAW_PENDING 1
279: #define RESIZE_PENDING 2
280:
281: /*
282: * Configuration specs valid for the menu as a whole:
283: */
284:
285: static Tk_ConfigSpec configSpecs[] = {
286: {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
287: DEF_MENU_ACTIVE_BG_COLOR, Tk_Offset(Menu, activeBorder),
288: TK_CONFIG_COLOR_ONLY},
289: {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
290: DEF_MENU_ACTIVE_BG_MONO, Tk_Offset(Menu, activeBorder),
291: TK_CONFIG_MONO_ONLY},
292: {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth", "BorderWidth",
293: DEF_MENU_ACTIVE_BORDER_WIDTH, Tk_Offset(Menu, activeBorderWidth), 0},
294: {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
295: DEF_MENU_ACTIVE_FG_COLOR, Tk_Offset(Menu, activeFg),
296: TK_CONFIG_COLOR_ONLY},
297: {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
298: DEF_MENU_ACTIVE_FG_MONO, Tk_Offset(Menu, activeFg),
299: TK_CONFIG_MONO_ONLY},
300: {TK_CONFIG_BORDER, "-background", "background", "Background",
301: DEF_MENU_BG_COLOR, Tk_Offset(Menu, border), TK_CONFIG_COLOR_ONLY},
302: {TK_CONFIG_BORDER, "-background", "background", "Background",
303: DEF_MENU_BG_MONO, Tk_Offset(Menu, border), TK_CONFIG_MONO_ONLY},
304: {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
305: (char *) NULL, 0, 0},
306: {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
307: (char *) NULL, 0, 0},
308: {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
309: DEF_MENU_BORDER_WIDTH, Tk_Offset(Menu, borderWidth), 0},
310: {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
311: DEF_MENU_CURSOR, Tk_Offset(Menu, cursor), TK_CONFIG_NULL_OK},
312: {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
313: "DisabledForeground", DEF_MENU_DISABLED_FG_COLOR,
314: Tk_Offset(Menu, disabledFg), TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
315: {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
316: "DisabledForeground", DEF_MENU_DISABLED_FG_MONO,
317: Tk_Offset(Menu, disabledFg), TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
318: {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
319: (char *) NULL, 0, 0},
320: {TK_CONFIG_FONT, "-font", "font", "Font",
321: DEF_MENU_FONT, Tk_Offset(Menu, fontPtr), 0},
322: {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
323: DEF_MENU_FG, Tk_Offset(Menu, fg), 0},
324: {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
325: DEF_MENU_SELECTOR_COLOR, Tk_Offset(Menu, selectorFg),
326: TK_CONFIG_COLOR_ONLY},
327: {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
328: DEF_MENU_SELECTOR_MONO, Tk_Offset(Menu, selectorFg),
329: TK_CONFIG_MONO_ONLY},
330: {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
331: (char *) NULL, 0, 0}
332: };
333:
334: /*
335: * Forward declarations for procedures defined later in this file:
336: */
337:
338: static int ActivateMenuEntry _ANSI_ARGS_((Menu *menuPtr,
339: int index));
340: static void ComputeMenuGeometry _ANSI_ARGS_((
341: ClientData clientData));
342: static int ConfigureMenu _ANSI_ARGS_((Tcl_Interp *interp,
343: Menu *menuPtr, int argc, char **argv,
344: int flags));
345: static int ConfigureMenuEntry _ANSI_ARGS_((Tcl_Interp *interp,
346: Menu *menuPtr, MenuEntry *mePtr, int index,
347: int argc, char **argv, int flags));
348: static void DestroyMenu _ANSI_ARGS_((ClientData clientData));
349: static void DestroyMenuEntry _ANSI_ARGS_((ClientData clientData));
350: static void DisplayMenu _ANSI_ARGS_((ClientData clientData));
351: static void EventuallyRedrawMenu _ANSI_ARGS_((Menu *menuPtr,
352: int index));
353: static int GetMenuIndex _ANSI_ARGS_((Tcl_Interp *interp,
354: Menu *menuPtr, char *string, int *indexPtr));
355: static void MenuEventProc _ANSI_ARGS_((ClientData clientData,
356: XEvent *eventPtr));
357: static char * MenuVarProc _ANSI_ARGS_((ClientData clientData,
358: Tcl_Interp *interp, char *name1, char *name2,
359: int flags));
360: static int MenuWidgetCmd _ANSI_ARGS_((ClientData clientData,
361: Tcl_Interp *interp, int argc, char **argv));
362: static int PostSubmenu _ANSI_ARGS_((Tcl_Interp *interp,
363: Menu *menuPtr, MenuEntry *mePtr));
364:
365: /*
366: *--------------------------------------------------------------
367: *
368: * Tk_MenuCmd --
369: *
370: * This procedure is invoked to process the "menu" Tcl
371: * command. See the user documentation for details on
372: * what it does.
373: *
374: * Results:
375: * A standard Tcl result.
376: *
377: * Side effects:
378: * See the user documentation.
379: *
380: *--------------------------------------------------------------
381: */
382:
383: int
384: Tk_MenuCmd(clientData, interp, argc, argv)
385: ClientData clientData; /* Main window associated with
386: * interpreter. */
387: Tcl_Interp *interp; /* Current interpreter. */
388: int argc; /* Number of arguments. */
389: char **argv; /* Argument strings. */
390: {
391: Tk_Window tkwin = (Tk_Window) clientData;
392: Tk_Window new;
393: register Menu *menuPtr;
394: XSetWindowAttributes atts;
395:
396: if (argc < 2) {
397: Tcl_AppendResult(interp, "wrong # args: should be \"",
398: argv[0], " pathName ?options?\"", (char *) NULL);
399: return TCL_ERROR;
400: }
401:
402: /*
403: * Create the new window. Set override-redirect so the window
404: * manager won't add a border or argue about placement, and set
405: * save-under so that the window can pop up and down without a
406: * lot of re-drawing.
407: */
408:
409: new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], "");
410: if (new == NULL) {
411: return TCL_ERROR;
412: }
413: atts.override_redirect = True;
414: atts.save_under = True;
415: Tk_ChangeWindowAttributes(new, CWOverrideRedirect|CWSaveUnder, &atts);
416:
417: /*
418: * Initialize the data structure for the menu.
419: */
420:
421: menuPtr = (Menu *) ckalloc(sizeof(Menu));
422: menuPtr->tkwin = new;
423: menuPtr->interp = interp;
424: menuPtr->entries = NULL;
425: menuPtr->numEntries = 0;
426: menuPtr->active = -1;
427: menuPtr->group = NULL;
428: menuPtr->border = NULL;
429: menuPtr->activeBorder = NULL;
430: menuPtr->fontPtr = NULL;
431: menuPtr->fg = NULL;
432: menuPtr->textGC = None;
433: menuPtr->disabledFg = NULL;
434: menuPtr->gray = None;
435: menuPtr->disabledGC = None;
436: menuPtr->activeFg = NULL;
437: menuPtr->activeGC = None;
438: menuPtr->selectorFg = NULL;
439: menuPtr->selectorGC = None;
440: menuPtr->cursor = None;
441: menuPtr->postedCascade = NULL;
442: menuPtr->flags = 0;
443:
444: Tk_SetClass(new, "Menu");
445: Tk_CreateEventHandler(menuPtr->tkwin, ExposureMask|StructureNotifyMask,
446: MenuEventProc, (ClientData) menuPtr);
447: Tcl_CreateCommand(interp, Tk_PathName(menuPtr->tkwin), MenuWidgetCmd,
448: (ClientData) menuPtr, (void (*)()) NULL);
449: if (ConfigureMenu(interp, menuPtr, argc-2, argv+2, 0) != TCL_OK) {
450: goto error;
451: }
452:
453: interp->result = Tk_PathName(menuPtr->tkwin);
454: return TCL_OK;
455:
456: error:
457: Tk_DestroyWindow(menuPtr->tkwin);
458: return TCL_ERROR;
459: }
460:
461: /*
462: *--------------------------------------------------------------
463: *
464: * MenuWidgetCmd --
465: *
466: * This procedure is invoked to process the Tcl command
467: * that corresponds to a widget managed by this module.
468: * See the user documentation for details on what it does.
469: *
470: * Results:
471: * A standard Tcl result.
472: *
473: * Side effects:
474: * See the user documentation.
475: *
476: *--------------------------------------------------------------
477: */
478:
479: static int
480: MenuWidgetCmd(clientData, interp, argc, argv)
481: ClientData clientData; /* Information about menu widget. */
482: Tcl_Interp *interp; /* Current interpreter. */
483: int argc; /* Number of arguments. */
484: char **argv; /* Argument strings. */
485: {
486: register Menu *menuPtr = (Menu *) clientData;
487: register MenuEntry *mePtr;
488: int result = TCL_OK;
489: int length, type;
490: char c;
491:
492: if (argc < 2) {
493: Tcl_AppendResult(interp, "wrong # args: should be \"",
494: argv[0], " option ?arg arg ...?\"", (char *) NULL);
495: return TCL_ERROR;
496: }
497: Tk_Preserve((ClientData) menuPtr);
498: c = argv[1][0];
499: length = strlen(argv[1]);
500: if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
501: && (length >= 2)) {
502: int index;
503:
504: if (argc != 3) {
505: Tcl_AppendResult(interp, "wrong # args: should be \"",
506: argv[0], " activate index\"", (char *) NULL);
507: goto error;
508: }
509: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
510: goto error;
511: }
512: if (menuPtr->active == index) {
513: goto done;
514: }
515: if (index >= 0) {
516: if ((menuPtr->entries[index]->type == SEPARATOR_ENTRY)
517: || (menuPtr->entries[index]->state == tkDisabledUid)) {
518: index = -1;
519: }
520: }
521: result = ActivateMenuEntry(menuPtr, index);
522: } else if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)
523: && (length >= 2)) {
524: MenuEntry **newEntries;
525:
526: if (argc < 3) {
527: Tcl_AppendResult(interp, "wrong # args: should be \"",
528: argv[0], " add type ?options?\"", (char *) NULL);
529: goto error;
530: }
531:
532: /*
533: * Figure out the type of the new entry.
534: */
535:
536: c = argv[2][0];
537: length = strlen(argv[2]);
538: if ((c == 'c') && (strncmp(argv[2], "cascade", length) == 0)
539: && (length >= 2)) {
540: type = CASCADE_ENTRY;
541: } else if ((c == 'c') && (strncmp(argv[2], "checkbutton", length) == 0)
542: && (length >= 2)) {
543: type = CHECK_BUTTON_ENTRY;
544: } else if ((c == 'c') && (strncmp(argv[2], "command", length) == 0)
545: && (length >= 2)) {
546: type = COMMAND_ENTRY;
547: } else if ((c == 'r')
548: && (strncmp(argv[2], "radiobutton", length) == 0)) {
549: type = RADIO_BUTTON_ENTRY;
550: } else if ((c == 's')
551: && (strncmp(argv[2], "separator", length) == 0)) {
552: type = SEPARATOR_ENTRY;
553: } else {
554: Tcl_AppendResult(interp, "bad menu entry type \"",
555: argv[2], "\": must be cascade, checkbutton, ",
556: "command, radiobutton, or separator", (char *) NULL);
557: goto error;
558: }
559:
560: /*
561: * Add a new entry to the end of the menu's array of entries,
562: * and process options for it.
563: */
564:
565: mePtr = (MenuEntry *) ckalloc(sizeof(MenuEntry));
566: newEntries = (MenuEntry **) ckalloc((unsigned)
567: ((menuPtr->numEntries+1)*sizeof(MenuEntry *)));
568: if (menuPtr->numEntries != 0) {
569: memcpy((VOID *) newEntries, (VOID *) menuPtr->entries,
570: menuPtr->numEntries*sizeof(MenuEntry *));
571: ckfree((char *) menuPtr->entries);
572: }
573: menuPtr->entries = newEntries;
574: menuPtr->entries[menuPtr->numEntries] = mePtr;
575: menuPtr->numEntries++;
576: mePtr->type = type;
577: mePtr->menuPtr = menuPtr;
578: mePtr->label = NULL;
579: mePtr->underline = -1;
580: mePtr->bitmap = None;
581: mePtr->accel = NULL;
582: mePtr->state = tkNormalUid;
583: mePtr->border = NULL;
584: mePtr->activeBorder = NULL;
585: mePtr->fontPtr = NULL;
586: mePtr->textGC = None;
587: mePtr->activeGC = None;
588: mePtr->disabledGC = None;
589: mePtr->command = NULL;
590: mePtr->name = NULL;
591: mePtr->onValue = NULL;
592: mePtr->offValue = NULL;
593: mePtr->flags = 0;
594: if (ConfigureMenuEntry(interp, menuPtr, mePtr, menuPtr->numEntries-1,
595: argc-3, argv+3, 0) != TCL_OK) {
596: DestroyMenuEntry((ClientData) mePtr);
597: menuPtr->numEntries--;
598: goto error;
599: }
600: } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
601: if (argc == 2) {
602: result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs,
603: (char *) menuPtr, (char *) NULL, 0);
604: } else if (argc == 3) {
605: result = Tk_ConfigureInfo(interp, menuPtr->tkwin, configSpecs,
606: (char *) menuPtr, argv[2], 0);
607: } else {
608: result = ConfigureMenu(interp, menuPtr, argc-2, argv+2,
609: TK_CONFIG_ARGV_ONLY);
610: }
611: } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
612: && (length >= 2)) {
613: int index, i;
614:
615: if (argc != 3) {
616: Tcl_AppendResult(interp, "wrong # args: should be \"",
617: argv[0], " delete index\"", (char *) NULL);
618: goto error;
619: }
620: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
621: goto error;
622: }
623: if (index < 0) {
624: goto done;
625: }
626: Tk_EventuallyFree((ClientData) menuPtr->entries[index],
627: DestroyMenuEntry);
628: for (i = index; i < menuPtr->numEntries-1; i++) {
629: menuPtr->entries[i] = menuPtr->entries[i+1];
630: }
631: menuPtr->numEntries -= 1;
632: if (menuPtr->active == index) {
633: menuPtr->active = -1;
634: } else if (menuPtr->active > index) {
635: menuPtr->active -= 1;
636: }
637: if (!(menuPtr->flags & RESIZE_PENDING)) {
638: menuPtr->flags |= RESIZE_PENDING;
639: Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
640: }
641: } else if ((c == 'd') && (strncmp(argv[1], "disable", length) == 0)
642: && (length >= 2)) {
643: int index;
644:
645: if (argc != 3) {
646: Tcl_AppendResult(interp, "wrong # args: should be \"",
647: argv[0], " disable index\"", (char *) NULL);
648: goto error;
649: }
650: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
651: goto error;
652: }
653: if (index < 0) {
654: goto done;
655: }
656: menuPtr->entries[index]->state = tkDisabledUid;
657: if (menuPtr->active == index) {
658: menuPtr->active = -1;
659: }
660: EventuallyRedrawMenu(menuPtr, index);
661: } else if ((c == 'e') && (length >= 3)
662: && (strncmp(argv[1], "enable", length) == 0)) {
663: int index;
664:
665: if (argc != 3) {
666: Tcl_AppendResult(interp, "wrong # args: should be \"",
667: argv[0], " enable index\"", (char *) NULL);
668: goto error;
669: }
670: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
671: goto error;
672: }
673: if (index < 0) {
674: goto done;
675: }
676: menuPtr->entries[index]->state = tkNormalUid;
677: EventuallyRedrawMenu(menuPtr, index);
678: } else if ((c == 'e') && (length >= 3)
679: && (strncmp(argv[1], "entryconfigure", length) == 0)) {
680: int index;
681:
682: if (argc < 3) {
683: Tcl_AppendResult(interp, "wrong # args: should be \"",
684: argv[0], " entryconfigure index ?option value ...?\"",
685: (char *) NULL);
686: goto error;
687: }
688: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
689: goto error;
690: }
691: if (index < 0) {
692: goto done;
693: }
694: mePtr = menuPtr->entries[index];
695: Tk_Preserve((ClientData) mePtr);
696: if (argc == 3) {
697: result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs,
698: (char *) mePtr, (char *) NULL,
699: COMMAND_MASK << mePtr->type);
700: } else if (argc == 4) {
701: result = Tk_ConfigureInfo(interp, menuPtr->tkwin, entryConfigSpecs,
702: (char *) mePtr, argv[3], COMMAND_MASK << mePtr->type);
703: } else {
704: result = ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc-3,
705: argv+3, TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
706: }
707: Tk_Release((ClientData) mePtr);
708: } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
709: && (length >= 3)) {
710: int index;
711:
712: if (argc != 3) {
713: Tcl_AppendResult(interp, "wrong # args: should be \"",
714: argv[0], " index string\"", (char *) NULL);
715: goto error;
716: }
717: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
718: goto error;
719: }
720: if (index < 0) {
721: interp->result = "none";
722: } else {
723: sprintf(interp->result, "%d", index);
724: }
725: } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
726: && (length >= 3)) {
727: int index;
728:
729: if (argc != 3) {
730: Tcl_AppendResult(interp, "wrong # args: should be \"",
731: argv[0], " invoke index\"", (char *) NULL);
732: goto error;
733: }
734: if (GetMenuIndex(interp, menuPtr, argv[2], &index) != TCL_OK) {
735: goto error;
736: }
737: if (index < 0) {
738: goto done;
739: }
740: mePtr = menuPtr->entries[index];
741: if (mePtr->state == tkDisabledUid) {
742: goto done;
743: }
744: Tk_Preserve((ClientData) mePtr);
745: if (mePtr->type == CHECK_BUTTON_ENTRY) {
746: if (mePtr->flags & ENTRY_SELECTED) {
747: Tcl_SetVar(interp, mePtr->name, mePtr->offValue,
748: TCL_GLOBAL_ONLY);
749: } else {
750: Tcl_SetVar(interp, mePtr->name, mePtr->onValue,
751: TCL_GLOBAL_ONLY);
752: }
753: } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
754: Tcl_SetVar(interp, mePtr->name, mePtr->onValue, TCL_GLOBAL_ONLY);
755: }
756: if (mePtr->command != NULL) {
757: result = Tcl_GlobalEval(interp, mePtr->command);
758: }
759: Tk_Release((ClientData) mePtr);
760: } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) {
761: int x, y, tmp;
762: Tk_Uid group;
763:
764: if ((argc != 4) && (argc != 5)) {
765: Tcl_AppendResult(interp, "wrong # args: should be \"",
766: argv[0], " post x y ?group?\"", (char *) NULL);
767: goto error;
768: }
769: if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
770: || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
771: goto error;
772: }
773: if (argc == 5) {
774: group = Tk_GetUid(argv[4]);
775: } else {
776: group = Tk_GetUid("default");
777: }
778:
779: /*
780: * Adjust the position of the menu if necessary to keep it
781: * on-screen.
782: */
783:
784: tmp = WidthOfScreen(Tk_Screen(menuPtr->tkwin))
785: - Tk_Width(menuPtr->tkwin);
786: if (x > tmp) {
787: x = tmp;
788: }
789: if (x < 0) {
790: x = 0;
791: }
792: tmp = HeightOfScreen(Tk_Screen(menuPtr->tkwin))
793: - Tk_Height(menuPtr->tkwin);
794: if (y > tmp) {
795: y = tmp;
796: }
797: if (y < 0) {
798: y = 0;
799: }
800: if ((x != Tk_X(menuPtr->tkwin)) || (y != Tk_Y(menuPtr->tkwin))) {
801: Tk_MoveWindow(menuPtr->tkwin, x, y);
802: }
803: if (Tk_IsMapped(menuPtr->tkwin)) {
804: if (group != menuPtr->group) {
805: Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
806: Tk_ShareEvents(menuPtr->tkwin, group);
807: }
808: } else {
809: Tk_ShareEvents(menuPtr->tkwin, group);
810: Tk_MapWindow(menuPtr->tkwin);
811: result = ActivateMenuEntry(menuPtr, -1);
812: }
813: XRaiseWindow(Tk_Display(menuPtr->tkwin), Tk_WindowId(menuPtr->tkwin));
814: menuPtr->group = group;
815: } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
816: if (argc != 2) {
817: Tcl_AppendResult(interp, "wrong # args: should be \"",
818: argv[0], " unpost\"", (char *) NULL);
819: goto error;
820: }
821: Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
822: Tk_UnmapWindow(menuPtr->tkwin);
823: result = ActivateMenuEntry(menuPtr, -1);
824: if (result == TCL_OK) {
825: result = PostSubmenu(interp, menuPtr, (MenuEntry *) NULL);
826: }
827: } else {
828: Tcl_AppendResult(interp, "bad option \"", argv[1],
829: "\": must be activate, add, configure, delete, disable, ",
830: "enable, entryconfigure, index, invoke, post, ",
831: "or unpost", (char *) NULL);
832: goto error;
833: }
834: done:
835: Tk_Release((ClientData) menuPtr);
836: return result;
837:
838: error:
839: Tk_Release((ClientData) menuPtr);
840: return TCL_ERROR;
841: }
842:
843: /*
844: *----------------------------------------------------------------------
845: *
846: * DestroyMenu --
847: *
848: * This procedure is invoked by Tk_EventuallyFree or Tk_Release
849: * to clean up the internal structure of a menu at a safe time
850: * (when no-one is using it anymore).
851: *
852: * Results:
853: * None.
854: *
855: * Side effects:
856: * Everything associated with the menu is freed up.
857: *
858: *----------------------------------------------------------------------
859: */
860:
861: static void
862: DestroyMenu(clientData)
863: ClientData clientData; /* Info about menu widget. */
864: {
865: register Menu *menuPtr = (Menu *) clientData;
866: int i;
867:
868: for (i = 0; i < menuPtr->numEntries; i++) {
869: DestroyMenuEntry((ClientData) menuPtr->entries[i]);
870: }
871: if (menuPtr->entries != NULL) {
872: ckfree((char *) menuPtr->entries);
873: }
874: if (menuPtr->border != NULL) {
875: Tk_Free3DBorder(menuPtr->border);
876: }
877: if (menuPtr->activeBorder != NULL) {
878: Tk_Free3DBorder(menuPtr->activeBorder);
879: }
880: if (menuPtr->fontPtr != NULL) {
881: Tk_FreeFontStruct(menuPtr->fontPtr);
882: }
883: if (menuPtr->fg != NULL) {
884: Tk_FreeColor(menuPtr->fg);
885: }
886: if (menuPtr->textGC != None) {
887: Tk_FreeGC(menuPtr->textGC);
888: }
889: if (menuPtr->disabledFg != NULL) {
890: Tk_FreeColor(menuPtr->disabledFg);
891: }
892: if (menuPtr->gray != None) {
893: Tk_FreeBitmap(menuPtr->gray);
894: }
895: if (menuPtr->disabledGC != None) {
896: Tk_FreeGC(menuPtr->disabledGC);
897: }
898: if (menuPtr->activeFg != NULL) {
899: Tk_FreeColor(menuPtr->activeFg);
900: }
901: if (menuPtr->activeGC != None) {
902: Tk_FreeGC(menuPtr->activeGC);
903: }
904: if (menuPtr->selectorFg != NULL) {
905: Tk_FreeColor(menuPtr->selectorFg);
906: }
907: if (menuPtr->selectorGC != None) {
908: Tk_FreeGC(menuPtr->selectorGC);
909: }
910: if (menuPtr->cursor != None) {
911: Tk_FreeCursor(menuPtr->cursor);
912: }
913: ckfree((char *) menuPtr);
914: }
915:
916: /*
917: *----------------------------------------------------------------------
918: *
919: * DestroyMenuEntry --
920: *
921: * This procedure is invoked by Tk_EventuallyFree or Tk_Release
922: * to clean up the internal structure of a menu entry at a safe time
923: * (when no-one is using it anymore).
924: *
925: * Results:
926: * None.
927: *
928: * Side effects:
929: * Everything associated with the menu entry is freed up.
930: *
931: *----------------------------------------------------------------------
932: */
933:
934: static void
935: DestroyMenuEntry(clientData)
936: ClientData clientData; /* Pointer to entry to be freed. */
937: {
938: register MenuEntry *mePtr = (MenuEntry *) clientData;
939: Menu *menuPtr = mePtr->menuPtr;
940:
941: if (mePtr->name != NULL) {
942: Tcl_UntraceVar(menuPtr->interp, mePtr->name,
943: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
944: MenuVarProc, (ClientData) mePtr);
945: }
946: if (menuPtr->postedCascade == mePtr) {
947: if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL)
948: != TCL_OK) {
949: TkBindError(menuPtr->interp);
950: }
951: }
952: if (mePtr->label != NULL) {
953: ckfree(mePtr->label);
954: }
955: if (mePtr->bitmap != None) {
956: #if defined(USE_XPM3)
957: Tk_FreePixmap(mePtr->bitmap);
958: #else
959: Tk_FreeBitmap(mePtr->bitmap);
960: #endif
961: }
962: if (mePtr->accel != NULL) {
963: ckfree(mePtr->accel);
964: }
965: if (mePtr->border != NULL) {
966: Tk_Free3DBorder(mePtr->border);
967: }
968: if (mePtr->activeBorder != NULL) {
969: Tk_Free3DBorder(mePtr->activeBorder);
970: }
971: if (mePtr->fontPtr != NULL) {
972: Tk_FreeFontStruct(mePtr->fontPtr);
973: }
974: if (mePtr->textGC != NULL) {
975: Tk_FreeGC(mePtr->textGC);
976: }
977: if (mePtr->activeGC != NULL) {
978: Tk_FreeGC(mePtr->activeGC);
979: }
980: if (mePtr->disabledGC != NULL) {
981: Tk_FreeGC(mePtr->disabledGC);
982: }
983: if (mePtr->command != NULL) {
984: ckfree(mePtr->command);
985: }
986: if (mePtr->name != NULL) {
987: ckfree(mePtr->name);
988: }
989: if (mePtr->onValue != NULL) {
990: ckfree(mePtr->onValue);
991: }
992: if (mePtr->offValue != NULL) {
993: ckfree(mePtr->offValue);
994: }
995: ckfree((char *) mePtr);
996: }
997:
998: /*
999: *----------------------------------------------------------------------
1000: *
1001: * ConfigureMenu --
1002: *
1003: * This procedure is called to process an argv/argc list, plus
1004: * the Tk option database, in order to configure (or
1005: * reconfigure) a menu widget.
1006: *
1007: * Results:
1008: * The return value is a standard Tcl result. If TCL_ERROR is
1009: * returned, then interp->result contains an error message.
1010: *
1011: * Side effects:
1012: * Configuration information, such as colors, font, etc. get set
1013: * for menuPtr; old resources get freed, if there were any.
1014: *
1015: *----------------------------------------------------------------------
1016: */
1017:
1018: static int
1019: ConfigureMenu(interp, menuPtr, argc, argv, flags)
1020: Tcl_Interp *interp; /* Used for error reporting. */
1021: register Menu *menuPtr; /* Information about widget; may or may
1022: * not already have values for some fields. */
1023: int argc; /* Number of valid entries in argv. */
1024: char **argv; /* Arguments. */
1025: int flags; /* Flags to pass to Tk_ConfigureWidget. */
1026: {
1027: XGCValues gcValues;
1028: GC newGC;
1029: unsigned long mask;
1030: int i;
1031:
1032: if (Tk_ConfigureWidget(interp, menuPtr->tkwin, configSpecs,
1033: argc, argv, (char *) menuPtr, flags) != TCL_OK) {
1034: return TCL_ERROR;
1035: }
1036:
1037: /*
1038: * A few options need special processing, such as setting the
1039: * background from a 3-D border, or filling in complicated
1040: * defaults that couldn't be specified to Tk_ConfigureWidget.
1041: */
1042:
1043: Tk_SetBackgroundFromBorder(menuPtr->tkwin, menuPtr->border);
1044:
1045: gcValues.font = menuPtr->fontPtr->fid;
1046: gcValues.foreground = menuPtr->fg->pixel;
1047: gcValues.background = Tk_3DBorderColor(menuPtr->border)->pixel;
1048: newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
1049: &gcValues);
1050: if (menuPtr->textGC != None) {
1051: Tk_FreeGC(menuPtr->textGC);
1052: }
1053: menuPtr->textGC = newGC;
1054:
1055: if (menuPtr->disabledFg != NULL) {
1056: gcValues.foreground = menuPtr->disabledFg->pixel;
1057: mask = GCForeground|GCBackground|GCFont;
1058: } else {
1059: gcValues.foreground = gcValues.background;
1060: if (menuPtr->gray == None) {
1061: menuPtr->gray = Tk_GetBitmap(interp, menuPtr->tkwin,
1062: Tk_GetUid("gray50"));
1063: if (menuPtr->gray == None) {
1064: return TCL_ERROR;
1065: }
1066: }
1067: gcValues.fill_style = FillStippled;
1068: gcValues.stipple = menuPtr->gray;
1069: mask = GCForeground|GCFillStyle|GCStipple;
1070: }
1071: newGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
1072: if (menuPtr->disabledGC != None) {
1073: Tk_FreeGC(menuPtr->disabledGC);
1074: }
1075: menuPtr->disabledGC = newGC;
1076:
1077: gcValues.font = menuPtr->fontPtr->fid;
1078: gcValues.foreground = menuPtr->activeFg->pixel;
1079: gcValues.background = Tk_3DBorderColor(menuPtr->activeBorder)->pixel;
1080: newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCBackground|GCFont,
1081: &gcValues);
1082: if (menuPtr->activeGC != None) {
1083: Tk_FreeGC(menuPtr->activeGC);
1084: }
1085: menuPtr->activeGC = newGC;
1086:
1087: gcValues.foreground = menuPtr->selectorFg->pixel;
1088: newGC = Tk_GetGC(menuPtr->tkwin, GCForeground|GCFont, &gcValues);
1089: if (menuPtr->selectorGC != None) {
1090: Tk_FreeGC(menuPtr->selectorGC);
1091: }
1092: menuPtr->selectorGC = newGC;
1093:
1094: /*
1095: * After reconfiguring a menu, we need to reconfigure all of the
1096: * entries in the menu, since some of the things in the children
1097: * (such as graphics contexts) may have to change to reflect changes
1098: * in the parent.
1099: */
1100:
1101: for (i = 0; i < menuPtr->numEntries; i++) {
1102: MenuEntry *mePtr;
1103:
1104: mePtr = menuPtr->entries[i];
1105: ConfigureMenuEntry(interp, menuPtr, mePtr, i, 0, (char **) NULL,
1106: TK_CONFIG_ARGV_ONLY | COMMAND_MASK << mePtr->type);
1107: }
1108:
1109: if (!(menuPtr->flags & RESIZE_PENDING)) {
1110: menuPtr->flags |= RESIZE_PENDING;
1111: Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
1112: }
1113:
1114: return TCL_OK;
1115: }
1116:
1117: /*
1118: *----------------------------------------------------------------------
1119: *
1120: * ConfigureMenuEntry --
1121: *
1122: * This procedure is called to process an argv/argc list, plus
1123: * the Tk option database, in order to configure (or
1124: * reconfigure) one entry in a menu.
1125: *
1126: * Results:
1127: * The return value is a standard Tcl result. If TCL_ERROR is
1128: * returned, then interp->result contains an error message.
1129: *
1130: * Side effects:
1131: * Configuration information such as label and accelerator get
1132: * set for mePtr; old resources get freed, if there were any.
1133: *
1134: *----------------------------------------------------------------------
1135: */
1136:
1137: static int
1138: ConfigureMenuEntry(interp, menuPtr, mePtr, index, argc, argv, flags)
1139: Tcl_Interp *interp; /* Used for error reporting. */
1140: Menu *menuPtr; /* Information about whole menu. */
1141: register MenuEntry *mePtr; /* Information about menu entry; may
1142: * or may not already have values for
1143: * some fields. */
1144: int index; /* Index of mePtr within menuPtr's
1145: * entries. */
1146: int argc; /* Number of valid entries in argv. */
1147: char **argv; /* Arguments. */
1148: int flags; /* Additional flags to pass to
1149: * Tk_ConfigureWidget. */
1150: {
1151: XGCValues gcValues;
1152: GC newGC, newActiveGC, newDisabledGC;
1153: unsigned long mask;
1154:
1155: /*
1156: * If this entry is a cascade and the cascade is posted, then unpost
1157: * it before reconfiguring the entry (otherwise the reconfigure might
1158: * change the name of the cascaded entry, leaving a posted menu
1159: * high and dry).
1160: */
1161:
1162: if (menuPtr->postedCascade == mePtr) {
1163: if (PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL)
1164: != TCL_OK) {
1165: TkBindError(menuPtr->interp);
1166: }
1167: }
1168:
1169: /*
1170: * If this entry is a check button or radio button, then remove
1171: * its old trace procedure.
1172: */
1173:
1174: if ((mePtr->name != NULL) &&
1175: ((mePtr->type == CHECK_BUTTON_ENTRY)
1176: || (mePtr->type == RADIO_BUTTON_ENTRY))) {
1177: Tcl_UntraceVar(menuPtr->interp, mePtr->name,
1178: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1179: MenuVarProc, (ClientData) mePtr);
1180: }
1181:
1182: if (Tk_ConfigureWidget(interp, menuPtr->tkwin, entryConfigSpecs,
1183: argc, argv, (char *) mePtr,
1184: flags | (COMMAND_MASK << mePtr->type)) != TCL_OK) {
1185: return TCL_ERROR;
1186: }
1187:
1188: /*
1189: * The code below handles special configuration stuff not taken
1190: * care of by Tk_ConfigureWidget, such as special processing for
1191: * defaults, sizing strings, graphics contexts, etc.
1192: */
1193:
1194: if (mePtr->label == NULL) {
1195: mePtr->labelLength = 0;
1196: } else {
1197: mePtr->labelLength = strlen(mePtr->label);
1198: }
1199: if (mePtr->accel == NULL) {
1200: mePtr->accelLength = 0;
1201: } else {
1202: mePtr->accelLength = strlen(mePtr->accel);
1203: }
1204:
1205: if (mePtr->state == tkActiveUid) {
1206: if (index != menuPtr->active) {
1207: ActivateMenuEntry(menuPtr, index);
1208: }
1209: } else {
1210: if (index == menuPtr->active) {
1211: ActivateMenuEntry(menuPtr, -1);
1212: }
1213: if ((mePtr->state != tkNormalUid) && (mePtr->state != tkDisabledUid)) {
1214: Tcl_AppendResult(interp, "bad state value \"", mePtr->state,
1215: "\": must be normal, active, or disabled", (char *) NULL);
1216: mePtr->state = tkNormalUid;
1217: return TCL_ERROR;
1218: }
1219: }
1220:
1221: if (mePtr->fontPtr != NULL) {
1222: gcValues.foreground = menuPtr->fg->pixel;
1223: gcValues.background = Tk_3DBorderColor(
1224: (mePtr->border != NULL) ? mePtr->border : menuPtr->border)
1225: ->pixel;
1226: gcValues.font = mePtr->fontPtr->fid;
1227:
1228: /*
1229: * Note: disable GraphicsExpose events; we know there won't be
1230: * obscured areas when copying from an off-screen pixmap to the
1231: * screen and this gets rid of unnecessary events.
1232: */
1233:
1234: gcValues.graphics_exposures = False;
1235: newGC = Tk_GetGC(menuPtr->tkwin,
1236: GCForeground|GCBackground|GCFont|GCGraphicsExposures,
1237: &gcValues);
1238:
1239: if (menuPtr->disabledFg != NULL) {
1240: gcValues.foreground = menuPtr->disabledFg->pixel;
1241: mask = GCForeground|GCBackground|GCFont|GCGraphicsExposures;
1242: } else {
1243: gcValues.foreground = gcValues.background;
1244: gcValues.fill_style = FillStippled;
1245: gcValues.stipple = menuPtr->gray;
1246: mask = GCForeground|GCFillStyle|GCStipple;
1247: }
1248: newDisabledGC = Tk_GetGC(menuPtr->tkwin, mask, &gcValues);
1249:
1250: gcValues.foreground = menuPtr->activeFg->pixel;
1251: gcValues.background = Tk_3DBorderColor(
1252: (mePtr->activeBorder != NULL) ? mePtr->activeBorder
1253: : menuPtr->activeBorder)->pixel;
1254: newActiveGC = Tk_GetGC(menuPtr->tkwin,
1255: GCForeground|GCBackground|GCFont|GCGraphicsExposures,
1256: &gcValues);
1257: } else {
1258: newGC = NULL;
1259: newActiveGC = NULL;
1260: newDisabledGC = NULL;
1261: }
1262: if (mePtr->textGC != NULL) {
1263: Tk_FreeGC(mePtr->textGC);
1264: }
1265: mePtr->textGC = newGC;
1266: if (mePtr->activeGC != NULL) {
1267: Tk_FreeGC(mePtr->activeGC);
1268: }
1269: mePtr->activeGC = newActiveGC;
1270: if (mePtr->disabledGC != NULL) {
1271: Tk_FreeGC(mePtr->disabledGC);
1272: }
1273: mePtr->disabledGC = newDisabledGC;
1274:
1275: if ((mePtr->type == CHECK_BUTTON_ENTRY)
1276: || (mePtr->type == RADIO_BUTTON_ENTRY)) {
1277: char *value;
1278:
1279: if (mePtr->name == NULL) {
1280: mePtr->name = ckalloc((unsigned) (strlen(mePtr->label) + 1));
1281: strcpy(mePtr->name, mePtr->label);
1282: }
1283: if (mePtr->onValue == NULL) {
1284: mePtr->onValue = ckalloc((unsigned) (strlen(mePtr->label) + 1));
1285: strcpy(mePtr->onValue, mePtr->label);
1286: }
1287:
1288: /*
1289: * Select the entry if the associated variable has the
1290: * appropriate value, initialize the variable if it doesn't
1291: * exist, then set a trace on the variable to monitor future
1292: * changes to its value.
1293: */
1294:
1295: value = Tcl_GetVar(interp, mePtr->name, TCL_GLOBAL_ONLY);
1296: mePtr->flags &= ENTRY_SELECTED;
1297: if (value != NULL) {
1298: if (strcmp(value, mePtr->onValue) == 0) {
1299: mePtr->flags |= ENTRY_SELECTED;
1300: }
1301: } else {
1302: Tcl_SetVar(interp, mePtr->name,
1303: (mePtr->type == CHECK_BUTTON_ENTRY) ? mePtr->offValue : "",
1304: TCL_GLOBAL_ONLY);
1305: }
1306: Tcl_TraceVar(interp, mePtr->name,
1307: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1308: MenuVarProc, (ClientData) mePtr);
1309: }
1310:
1311: if (!(menuPtr->flags & RESIZE_PENDING)) {
1312: menuPtr->flags |= RESIZE_PENDING;
1313: Tk_DoWhenIdle(ComputeMenuGeometry, (ClientData) menuPtr);
1314: }
1315: return TCL_OK;
1316: }
1317:
1318: /*
1319: *--------------------------------------------------------------
1320: *
1321: * ComputeMenuGeometry --
1322: *
1323: * This procedure is invoked to recompute the size and
1324: * layout of a menu. It is called as a when-idle handler so
1325: * that it only gets done once, even if a group of changes is
1326: * made to the menu.
1327: *
1328: * Results:
1329: * None.
1330: *
1331: * Side effects:
1332: * Fields of menu entries are changed to reflect their
1333: * current positions, and the size of the menu window
1334: * itself may be changed.
1335: *
1336: *--------------------------------------------------------------
1337: */
1338:
1339: static void
1340: ComputeMenuGeometry(clientData)
1341: ClientData clientData; /* Structure describing menu. */
1342: {
1343: Menu *menuPtr = (Menu *) clientData;
1344: register MenuEntry *mePtr;
1345: XFontStruct *fontPtr;
1346: int maxLabelWidth, maxSelectorWidth, maxAccelWidth;
1347: int width, height, selectorSpace, horizMargin;
1348: int i, y;
1349:
1350: if (menuPtr->tkwin == NULL) {
1351: return;
1352: }
1353:
1354: maxLabelWidth = maxSelectorWidth = maxAccelWidth = 0;
1355: y = menuPtr->borderWidth;
1356:
1357: for (i = 0; i < menuPtr->numEntries; i++) {
1358: mePtr = menuPtr->entries[i];
1359: selectorSpace = 0;
1360: fontPtr = mePtr->fontPtr;
1361: if (fontPtr == NULL) {
1362: fontPtr = menuPtr->fontPtr;
1363: }
1364:
1365: /*
1366: * For each entry, compute the height required by that
1367: * particular entry, plus three widths: the width of the
1368: * label, the width to allow for a selector to be displayed
1369: * to the left of the label (if any), and the width of the
1370: * accelerator to be displayed to the right of the label
1371: * (if any). These sizes depend, of course, on the type
1372: * of the entry.
1373: */
1374:
1375: if (mePtr->bitmap != None) {
1376: unsigned int bitmapWidth, bitmapHeight;
1377:
1378: #if defined(USE_XPM3)
1379: Tk_SizeOfPixmap(mePtr->bitmap, &bitmapWidth, &bitmapHeight);
1380: #else
1381: Tk_SizeOfBitmap(mePtr->bitmap, &bitmapWidth, &bitmapHeight);
1382: #endif
1383: mePtr->height = bitmapHeight;
1384: width = bitmapWidth;
1385: if (mePtr->type == CHECK_BUTTON_ENTRY) {
1386: selectorSpace = (14*mePtr->height)/10;
1387: mePtr->selectorDiameter = (65*mePtr->height)/100;
1388: } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
1389: selectorSpace = (14*mePtr->height)/10;
1390: mePtr->selectorDiameter = (75*mePtr->height)/100;
1391: }
1392: } else {
1393: mePtr->height = fontPtr->ascent + fontPtr->descent;
1394: if (mePtr->label != NULL) {
1395: (void) TkMeasureChars(fontPtr, mePtr->label,
1396: mePtr->labelLength, 0, (int) 100000,
1397: TK_NEWLINES_NOT_SPECIAL, &width);
1398: } else {
1399: width = 0;
1400: }
1401: if (mePtr->type == CHECK_BUTTON_ENTRY) {
1402: selectorSpace = mePtr->height;
1403: mePtr->selectorDiameter = (80*mePtr->height)/100;
1404: } else if (mePtr->type == RADIO_BUTTON_ENTRY) {
1405: selectorSpace = mePtr->height;
1406: mePtr->selectorDiameter = mePtr->height;
1407: }
1408: }
1409: mePtr->height += 2*menuPtr->activeBorderWidth + 2;
1410: if (width > maxLabelWidth) {
1411: maxLabelWidth = width;
1412: }
1413: if (mePtr->accel != NULL) {
1414: (void) TkMeasureChars(fontPtr, mePtr->accel, mePtr->accelLength,
1415: 0, (int) 100000, TK_NEWLINES_NOT_SPECIAL, &width);
1416: if (width > maxAccelWidth) {
1417: maxAccelWidth = width;
1418: }
1419: }
1420: if (mePtr->type == SEPARATOR_ENTRY) {
1421: mePtr->height = 4*menuPtr->borderWidth;
1422: }
1423: if (selectorSpace > maxSelectorWidth) {
1424: maxSelectorWidth = selectorSpace;
1425: }
1426: mePtr->y = y;
1427: y += mePtr->height;
1428: }
1429:
1430: /*
1431: * Got all the sizes. Update fields in the menu structure, then
1432: * resize the window if necessary. Leave margins on either side
1433: * of the selector (or just one margin if there is no selector).
1434: * Leave another margin on the right side of the label, plus yet
1435: * another margin to the right of the accelerator (if there is one).
1436: */
1437:
1438: horizMargin = 2;
1439: menuPtr->selectorSpace = maxSelectorWidth + horizMargin;
1440: if (maxSelectorWidth != 0) {
1441: menuPtr->selectorSpace += horizMargin;
1442: }
1443: menuPtr->labelWidth = maxLabelWidth + horizMargin;
1444: width = menuPtr->selectorSpace + menuPtr->labelWidth + maxAccelWidth
1445: + 2*menuPtr->borderWidth + 2*menuPtr->activeBorderWidth + 2;
1446: if (maxAccelWidth != 0) {
1447: width += horizMargin;
1448: }
1449: height = y + menuPtr->borderWidth;
1450:
1451: /*
1452: * The X server doesn't like zero dimensions, so round up to at least
1453: * 1 (a zero-sized menu should never really occur, anyway).
1454: */
1455:
1456: if (width <= 0) {
1457: width = 1;
1458: }
1459: if (height <= 0) {
1460: height = 1;
1461: }
1462: if ((width != Tk_ReqWidth(menuPtr->tkwin)) ||
1463: (height != Tk_ReqHeight(menuPtr->tkwin))) {
1464: Tk_GeometryRequest(menuPtr->tkwin, width, height);
1465: } else {
1466: /*
1467: * Must always force a redisplay here if the window is mapped
1468: * (even if the size didn't change, something else might have
1469: * changed in the menu, such as a label or accelerator). The
1470: * resize will force a redisplay above.
1471: */
1472:
1473: EventuallyRedrawMenu(menuPtr, -1);
1474: }
1475:
1476: menuPtr->flags &= ~RESIZE_PENDING;
1477: }
1478:
1479: /*
1480: *----------------------------------------------------------------------
1481: *
1482: * DisplayMenu --
1483: *
1484: * This procedure is invoked to display a menu widget.
1485: *
1486: * Results:
1487: * None.
1488: *
1489: * Side effects:
1490: * Commands are output to X to display the menu in its
1491: * current mode.
1492: *
1493: *----------------------------------------------------------------------
1494: */
1495:
1496: static void
1497: DisplayMenu(clientData)
1498: ClientData clientData; /* Information about widget. */
1499: {
1500: register Menu *menuPtr = (Menu *) clientData;
1501: register MenuEntry *mePtr;
1502: register Tk_Window tkwin = menuPtr->tkwin;
1503: XFontStruct *fontPtr;
1504: int index, baseline;
1505: GC gc;
1506:
1507: menuPtr->flags &= ~REDRAW_PENDING;
1508: if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1509: return;
1510: }
1511:
1512: /*
1513: * Loop through all of the entries, drawing them one at a time.
1514: */
1515:
1516: for (index = 0; index < menuPtr->numEntries; index++) {
1517: mePtr = menuPtr->entries[index];
1518: if (!(mePtr->flags & ENTRY_NEEDS_REDISPLAY)) {
1519: continue;
1520: }
1521: mePtr->flags &= ~ENTRY_NEEDS_REDISPLAY;
1522:
1523: /*
1524: * Background.
1525: */
1526:
1527: if (mePtr->state == tkActiveUid) {
1528: Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1529: (mePtr->activeBorder != NULL) ? mePtr->activeBorder
1530: : menuPtr->activeBorder, menuPtr->borderWidth, mePtr->y,
1531: Tk_Width(tkwin) - 2*menuPtr->borderWidth, mePtr->height,
1532: menuPtr->activeBorderWidth, TK_RELIEF_RAISED);
1533: gc = mePtr->activeGC;
1534: if (gc == NULL) {
1535: gc = menuPtr->activeGC;
1536: }
1537: } else {
1538: Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1539: (mePtr->border != NULL) ? mePtr->border
1540: : menuPtr->border, menuPtr->borderWidth, mePtr->y,
1541: Tk_Width(tkwin) - 2*menuPtr->borderWidth, mePtr->height,
1542: 0, TK_RELIEF_FLAT);
1543: if ((mePtr->state == tkDisabledUid)
1544: && (menuPtr->disabledFg != NULL)) {
1545: gc = mePtr->disabledGC;
1546: if (gc == NULL) {
1547: gc = menuPtr->disabledGC;
1548: }
1549: } else {
1550: gc = mePtr->textGC;
1551: if (gc == NULL) {
1552: gc = menuPtr->textGC;
1553: }
1554: }
1555: }
1556:
1557: /*
1558: * Draw label or bitmap for entry.
1559: */
1560:
1561: fontPtr = mePtr->fontPtr;
1562: if (fontPtr == NULL) {
1563: fontPtr = menuPtr->fontPtr;
1564: }
1565: baseline = mePtr->y + (mePtr->height + fontPtr->ascent
1566: - fontPtr->descent)/2;
1567: if (mePtr->bitmap != None) {
1568: unsigned int width, height;
1569:
1570: #if defined(USE_XPM3)
1571: Tk_SizeOfPixmap(mePtr->bitmap, &width, &height);
1572: XCopyArea(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin),
1573: gc, 0, 0, width, height,
1574: menuPtr->borderWidth + menuPtr->selectorSpace,
1575: (int) (mePtr->y + (mePtr->height - height)/2));
1576: #else
1577: Tk_SizeOfBitmap(mePtr->bitmap, &width, &height);
1578: XCopyPlane(Tk_Display(tkwin), mePtr->bitmap, Tk_WindowId(tkwin),
1579: gc, 0, 0, width, height,
1580: menuPtr->borderWidth + menuPtr->selectorSpace,
1581: (int) (mePtr->y + (mePtr->height - height)/2), 1);
1582: #endif
1583: } else {
1584: baseline = mePtr->y + (mePtr->height + fontPtr->ascent
1585: - fontPtr->descent)/2;
1586: if (mePtr->label != NULL) {
1587: TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
1588: fontPtr, mePtr->label, mePtr->labelLength,
1589: menuPtr->borderWidth + menuPtr->selectorSpace,
1590: baseline, TK_NEWLINES_NOT_SPECIAL);
1591: if (mePtr->underline >= 0) {
1592: TkUnderlineChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
1593: fontPtr, mePtr->label,
1594: menuPtr->borderWidth + menuPtr->selectorSpace,
1595: baseline, TK_NEWLINES_NOT_SPECIAL,
1596: mePtr->underline, mePtr->underline);
1597: }
1598: }
1599: }
1600:
1601: /*
1602: * Draw accelerator.
1603: */
1604:
1605: if (mePtr->accel != NULL) {
1606: TkDisplayChars(Tk_Display(tkwin), Tk_WindowId(tkwin), gc,
1607: fontPtr, mePtr->accel, mePtr->accelLength,
1608: menuPtr->borderWidth + menuPtr->selectorSpace
1609: + menuPtr->labelWidth, baseline, TK_NEWLINES_NOT_SPECIAL);
1610: }
1611:
1612: /*
1613: * Draw check-button selector.
1614: */
1615:
1616: if (mePtr->type == CHECK_BUTTON_ENTRY) {
1617: int dim, x, y;
1618:
1619: dim = mePtr->selectorDiameter;
1620: x = menuPtr->borderWidth + (menuPtr->selectorSpace - dim)/2;
1621: y = mePtr->y + (mePtr->height - dim)/2;
1622: Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1623: menuPtr->border, x, y, dim, dim,
1624: menuPtr->activeBorderWidth, TK_RELIEF_SUNKEN);
1625: x += menuPtr->activeBorderWidth;
1626: y += menuPtr->activeBorderWidth;
1627: dim -= 2*menuPtr->activeBorderWidth;
1628: if ((dim > 0) && (mePtr->flags & ENTRY_SELECTED)) {
1629: XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1630: menuPtr->selectorGC, x, y, (unsigned int) dim,
1631: (unsigned int) dim);
1632: }
1633: }
1634:
1635: /*
1636: * Draw radio-button selector.
1637: */
1638:
1639: if (mePtr->type == RADIO_BUTTON_ENTRY) {
1640: XPoint points[4];
1641: int radius;
1642:
1643: radius = mePtr->selectorDiameter/2;
1644: points[0].x = menuPtr->borderWidth
1645: + (menuPtr->selectorSpace - mePtr->selectorDiameter)/2;
1646: points[0].y = mePtr->y + (mePtr->height)/2;
1647: points[1].x = points[0].x + radius;
1648: points[1].y = points[0].y + radius;
1649: points[2].x = points[1].x + radius;
1650: points[2].y = points[0].y;
1651: points[3].x = points[1].x;
1652: points[3].y = points[0].y - radius;
1653: if (mePtr->flags & ENTRY_SELECTED) {
1654: XFillPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
1655: menuPtr->selectorGC, points, 4, Convex,
1656: CoordModeOrigin);
1657: } else {
1658: Tk_Fill3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
1659: menuPtr->border, points, 4, menuPtr->activeBorderWidth,
1660: TK_RELIEF_FLAT);
1661: }
1662: Tk_Draw3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
1663: menuPtr->border, points, 4, menuPtr->activeBorderWidth,
1664: TK_RELIEF_SUNKEN);
1665: }
1666:
1667: /*
1668: * Draw separator.
1669: */
1670:
1671: if (mePtr->type == SEPARATOR_ENTRY) {
1672: XPoint points[2];
1673: int margin;
1674:
1675: margin = (fontPtr->ascent + fontPtr->descent)/2;
1676: points[0].x = 2*menuPtr->borderWidth + margin;
1677: points[0].y = mePtr->y + mePtr->height/2;
1678: points[1].x = Tk_Width(tkwin) - 2*menuPtr->borderWidth - margin;
1679: points[1].y = points[0].y;
1680: Tk_Draw3DPolygon(Tk_Display(tkwin), Tk_WindowId(tkwin),
1681: menuPtr->border, points, 2, 1, TK_RELIEF_RAISED);
1682: }
1683:
1684: /*
1685: * If the entry is disabled with a stipple rather than a special
1686: * foreground color, generate the stippled effect.
1687: */
1688:
1689: if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg == NULL)) {
1690: XFillRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1691: menuPtr->disabledGC, menuPtr->borderWidth,
1692: mePtr->y,
1693: (unsigned) (Tk_Width(tkwin) - 2*menuPtr->borderWidth),
1694: (unsigned) mePtr->height);
1695: }
1696: }
1697:
1698: Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1699: menuPtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
1700: menuPtr->borderWidth, TK_RELIEF_RAISED);
1701: }
1702:
1703: /*
1704: *--------------------------------------------------------------
1705: *
1706: * GetMenuIndex --
1707: *
1708: * Parse a textual index into a menu and return the numerical
1709: * index of the indicated entry.
1710: *
1711: * Results:
1712: * A standard Tcl result. If all went well, then *indexPtr is
1713: * filled in with the entry index corresponding to string
1714: * (ranges from -1 to the number of entries in the menu minus
1715: * one). Otherwise an error message is left in interp->result.
1716: *
1717: * Side effects:
1718: * None.
1719: *
1720: *--------------------------------------------------------------
1721: */
1722:
1723: static int
1724: GetMenuIndex(interp, menuPtr, string, indexPtr)
1725: Tcl_Interp *interp; /* For error messages. */
1726: Menu *menuPtr; /* Menu for which the index is being
1727: * specified. */
1728: char *string; /* Specification of an entry in menu. See
1729: * manual entry for valid .*/
1730: int *indexPtr; /* Where to store converted relief. */
1731: {
1732: int i, y;
1733:
1734: if ((string[0] == 'a') && (strcmp(string, "active") == 0)) {
1735: *indexPtr = menuPtr->active;
1736: return TCL_OK;
1737: }
1738:
1739: if ((string[0] == 'l') && (strcmp(string, "last") == 0)) {
1740: *indexPtr = menuPtr->numEntries-1;
1741: return TCL_OK;
1742: }
1743:
1744: if ((string[0] == 'n') && (strcmp(string, "none") == 0)) {
1745: *indexPtr = -1;
1746: return TCL_OK;
1747: }
1748:
1749: if (string[0] == '@') {
1750: if (Tcl_GetInt(interp, string+1, &y) == TCL_OK) {
1751: if (y < 0) {
1752: *indexPtr = -1;
1753: return TCL_OK;
1754: }
1755: for (i = 0; i < menuPtr->numEntries; i++) {
1756: y -= menuPtr->entries[i]->height;
1757: if (y < 0) {
1758: break;
1759: }
1760: }
1761: if (i >= menuPtr->numEntries) {
1762: i = -1;
1763: }
1764: *indexPtr = i;
1765: return TCL_OK;
1766: } else {
1767: Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1768: }
1769: }
1770:
1771: if (isdigit(string[0])) {
1772: if (Tcl_GetInt(interp, string, &i) == TCL_OK) {
1773: if ((i < menuPtr->numEntries) && (i >= 0)) {
1774: *indexPtr = i;
1775: return TCL_OK;
1776: }
1777: } else {
1778: Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1779: }
1780: }
1781:
1782: for (i = 0; i < menuPtr->numEntries; i++) {
1783: char *label;
1784:
1785: label = menuPtr->entries[i]->label;
1786: if ((label != NULL)
1787: && (Tcl_StringMatch(menuPtr->entries[i]->label, string))) {
1788: *indexPtr = i;
1789: return TCL_OK;
1790: }
1791: }
1792:
1793: Tcl_AppendResult(interp, "bad menu entry index \"",
1794: string, "\"", (char *) NULL);
1795: return TCL_ERROR;
1796: }
1797:
1798: /*
1799: *--------------------------------------------------------------
1800: *
1801: * MenuEventProc --
1802: *
1803: * This procedure is invoked by the Tk dispatcher for various
1804: * events on menus.
1805: *
1806: * Results:
1807: * None.
1808: *
1809: * Side effects:
1810: * When the window gets deleted, internal structures get
1811: * cleaned up. When it gets exposed, it is redisplayed.
1812: *
1813: *--------------------------------------------------------------
1814: */
1815:
1816: static void
1817: MenuEventProc(clientData, eventPtr)
1818: ClientData clientData; /* Information about window. */
1819: XEvent *eventPtr; /* Information about event. */
1820: {
1821: Menu *menuPtr = (Menu *) clientData;
1822: if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1823: EventuallyRedrawMenu(menuPtr, -1);
1824: } else if (eventPtr->type == DestroyNotify) {
1825: Tcl_DeleteCommand(menuPtr->interp, Tk_PathName(menuPtr->tkwin));
1826:
1827: /*
1828: * Careful! Must delete the event-sharing information here
1829: * rather than in DestroyMenu. By the time that procedure
1830: * is called the tkwin may have been reused, resulting in some
1831: * other window accidentally being cut off from shared events.
1832: */
1833:
1834: Tk_UnshareEvents(menuPtr->tkwin, menuPtr->group);
1835: menuPtr->tkwin = NULL;
1836: if (menuPtr->flags & REDRAW_PENDING) {
1837: Tk_CancelIdleCall(DisplayMenu, (ClientData) menuPtr);
1838: }
1839: if (menuPtr->flags & RESIZE_PENDING) {
1840: Tk_CancelIdleCall(ComputeMenuGeometry, (ClientData) menuPtr);
1841: }
1842: Tk_EventuallyFree((ClientData) menuPtr, DestroyMenu);
1843: }
1844: }
1845:
1846: /*
1847: *--------------------------------------------------------------
1848: *
1849: * MenuVarProc --
1850: *
1851: * This procedure is invoked when someone changes the
1852: * state variable associated with a radiobutton or checkbutton
1853: * menu entry. The entry's selected state is set to match
1854: * the value of the variable.
1855: *
1856: * Results:
1857: * NULL is always returned.
1858: *
1859: * Side effects:
1860: * The menu entry may become selected or deselected.
1861: *
1862: *--------------------------------------------------------------
1863: */
1864:
1865: /* ARGSUSED */
1866: static char *
1867: MenuVarProc(clientData, interp, name1, name2, flags)
1868: ClientData clientData; /* Information about menu entry. */
1869: Tcl_Interp *interp; /* Interpreter containing variable. */
1870: char *name1; /* First part of variable's name. */
1871: char *name2; /* Second part of variable's name. */
1872: int flags; /* Describes what just happened. */
1873: {
1874: MenuEntry *mePtr = (MenuEntry *) clientData;
1875: Menu *menuPtr;
1876: char *value;
1877:
1878: menuPtr = mePtr->menuPtr;
1879:
1880: /*
1881: * If the variable is being unset, then re-establish the
1882: * trace unless the whole interpreter is going away.
1883: */
1884:
1885: if (flags & TCL_TRACE_UNSETS) {
1886: mePtr->flags &= ~ENTRY_SELECTED;
1887: if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1888: Tcl_TraceVar2(interp, name1, name2,
1889: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1890: MenuVarProc, clientData);
1891: }
1892: EventuallyRedrawMenu(menuPtr, -1);
1893: return (char *) NULL;
1894: }
1895:
1896: /*
1897: * Use the value of the variable to update the selected status of
1898: * the menu entry.
1899: */
1900:
1901: value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1902: if (strcmp(value, mePtr->onValue) == 0) {
1903: if (mePtr->flags & ENTRY_SELECTED) {
1904: return (char *) NULL;
1905: }
1906: mePtr->flags |= ENTRY_SELECTED;
1907: } else if (mePtr->flags & ENTRY_SELECTED) {
1908: mePtr->flags &= ~ENTRY_SELECTED;
1909: } else {
1910: return (char *) NULL;
1911: }
1912: EventuallyRedrawMenu(menuPtr, -1);
1913: return (char *) NULL;
1914: }
1915:
1916: /*
1917: *----------------------------------------------------------------------
1918: *
1919: * EventuallyRedrawMenu --
1920: *
1921: * Arrange for an entry of a menu, or the whole menu, to be
1922: * redisplayed at some point in the future.
1923: *
1924: * Results:
1925: * None.
1926: *
1927: * Side effects:
1928: * A when-idle hander is scheduled to do the redisplay, if there
1929: * isn't one already scheduled.
1930: *
1931: *----------------------------------------------------------------------
1932: */
1933:
1934: static void
1935: EventuallyRedrawMenu(menuPtr, index)
1936: register Menu *menuPtr; /* Information about menu to redraw. */
1937: int index; /* Which entry to redraw. If -1, then
1938: * all the entries in the menu are redrawn. */
1939: {
1940: if (menuPtr->tkwin == NULL) {
1941: return;
1942: }
1943: if (index != -1) {
1944: menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY;
1945: } else {
1946: for (index = 0; index < menuPtr->numEntries; index++) {
1947: menuPtr->entries[index]->flags |= ENTRY_NEEDS_REDISPLAY;
1948: }
1949: }
1950: if ((menuPtr->tkwin == NULL) || !Tk_IsMapped(menuPtr->tkwin)
1951: || (menuPtr->flags & REDRAW_PENDING)) {
1952: return;
1953: }
1954: Tk_DoWhenIdle(DisplayMenu, (ClientData) menuPtr);
1955: menuPtr->flags |= REDRAW_PENDING;
1956: }
1957:
1958: /*
1959: *--------------------------------------------------------------
1960: *
1961: * PostSubmenu --
1962: *
1963: * This procedure arranges for a particular submenu (i.e. the
1964: * menu corresponding to a given cascade entry) to be
1965: * posted.
1966: *
1967: * Results:
1968: * A standard Tcl return result. Errors may occur in the
1969: * Tcl commands generated to post and unpost submenus.
1970: *
1971: * Side effects:
1972: * If there is already a submenu posted, it is unposted.
1973: * The new submenu is then posted.
1974: *
1975: *--------------------------------------------------------------
1976: */
1977:
1978: static int
1979: PostSubmenu(interp, menuPtr, mePtr)
1980: Tcl_Interp *interp; /* Used for invoking sub-commands and
1981: * reporting errors. */
1982: register Menu *menuPtr; /* Information about menu as a whole. */
1983: register MenuEntry *mePtr; /* Info about submenu that is to be
1984: * posted. NULL means make sure that
1985: * no submenu is posted. */
1986: {
1987: char string[30];
1988: int result, x, y;
1989:
1990: if (mePtr == menuPtr->postedCascade) {
1991: return TCL_OK;
1992: }
1993:
1994: if (menuPtr->postedCascade != NULL) {
1995: result = Tcl_VarEval(interp, menuPtr->postedCascade->name,
1996: " unpost", (char *) NULL);
1997: menuPtr->postedCascade = NULL;
1998: if (result != TCL_OK) {
1999: return result;
2000: }
2001: }
2002:
2003: if ((mePtr != NULL) && (mePtr->name != NULL)) {
2004: Tk_GetRootCoords(menuPtr->tkwin, &x, &y);
2005: x += Tk_Width(menuPtr->tkwin);
2006: y += mePtr->y;
2007: sprintf(string, "%d %d ", x, y);
2008: result = Tcl_VarEval(interp, mePtr->name, " post ", string,
2009: menuPtr->group, (char *) NULL);
2010: if (result != TCL_OK) {
2011: return result;
2012: }
2013: menuPtr->postedCascade = mePtr;
2014: }
2015: return TCL_OK;
2016: }
2017:
2018: /*
2019: *----------------------------------------------------------------------
2020: *
2021: * ActivateMenuEntry --
2022: *
2023: * This procedure is invoked to make a particular menu entry
2024: * the active one, deactivating any other entry that might
2025: * currently be active.
2026: *
2027: * Results:
2028: * The return value is a standard Tcl result (errors can occur
2029: * while posting and unposting submenus).
2030: *
2031: * Side effects:
2032: * Menu entries get redisplayed, and the active entry changes.
2033: * Submenus may get posted and unposted.
2034: *
2035: *----------------------------------------------------------------------
2036: */
2037:
2038: static int
2039: ActivateMenuEntry(menuPtr, index)
2040: register Menu *menuPtr; /* Menu in which to activate. */
2041: int index; /* Index of entry to activate, or
2042: * -1 to deactivate all entries. */
2043: {
2044: register MenuEntry *mePtr;
2045: int result = TCL_OK;
2046:
2047: if (menuPtr->active >= 0) {
2048: mePtr = menuPtr->entries[menuPtr->active];
2049:
2050: /*
2051: * Don't change the state unless it's currently active (state
2052: * might already have been changed to disabled).
2053: */
2054:
2055: if (mePtr->state == tkActiveUid) {
2056: mePtr->state = tkNormalUid;
2057: }
2058: EventuallyRedrawMenu(menuPtr, menuPtr->active);
2059: }
2060: menuPtr->active = index;
2061: if (index >= 0) {
2062: mePtr = menuPtr->entries[index];
2063: mePtr->state = tkActiveUid;
2064: EventuallyRedrawMenu(menuPtr, index);
2065: Tk_Preserve((ClientData) mePtr);
2066: if (mePtr->type == CASCADE_ENTRY) {
2067: result = PostSubmenu(menuPtr->interp, menuPtr, mePtr);
2068: } else {
2069: result = PostSubmenu(menuPtr->interp, menuPtr, (MenuEntry *) NULL);
2070: }
2071: Tk_Release((ClientData) mePtr);
2072: }
2073: return result;
2074: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.