|
|
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.