|
|
1.1 ! root 1: /* ! 2: * tkMenubutton.c -- ! 3: * ! 4: * This module implements button-like widgets that are used ! 5: * to invoke pull-down menus. ! 6: * ! 7: * Copyright 1990 Regents of the University of California. ! 8: * Permission to use, copy, modify, and distribute this ! 9: * software and its documentation for any purpose and without ! 10: * fee is hereby granted, provided that the above copyright ! 11: * notice appear in all copies. The University of California ! 12: * makes no representations about the suitability of this ! 13: * software for any purpose. It is provided "as is" without ! 14: * express or implied warranty. ! 15: */ ! 16: ! 17: #ifndef lint ! 18: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenubutton.c,v 1.33 92/08/21 16:21:47 ouster Exp $ SPRITE (Berkeley)"; ! 19: #endif ! 20: ! 21: #include "tkconfig.h" ! 22: #include "default.h" ! 23: #include "tkint.h" ! 24: ! 25: /* ! 26: * A data structure of the following type is kept for each ! 27: * widget managed by this file: ! 28: */ ! 29: ! 30: typedef struct { ! 31: Tk_Window tkwin; /* Window that embodies the widget. NULL ! 32: * means that the window has been destroyed ! 33: * but the data structures haven't yet been ! 34: * cleaned up.*/ ! 35: Tcl_Interp *interp; /* Interpreter associated with menu button. */ ! 36: char *menuName; /* Name of menu associated with widget (used ! 37: * to generate post and unpost commands). ! 38: * Malloc-ed. */ ! 39: Tk_Uid varName; /* Name of variable associated with collection ! 40: * of menu bars: used to allow scanning of ! 41: * menus. Also used as identifier for ! 42: * menu group. */ ! 43: ! 44: /* ! 45: * Information about what's displayed in the menu button: ! 46: */ ! 47: ! 48: char *text; /* Text to display in button (malloc'ed) ! 49: * or NULL. */ ! 50: int textLength; /* # of characters in text. */ ! 51: int underline; /* Index of character to underline. */ ! 52: char *textVarName; /* Name of variable (malloc'ed) or NULL. ! 53: * If non-NULL, button displays the contents ! 54: * of this variable. */ ! 55: Pixmap bitmap; /* Bitmap to display or None. If not None ! 56: * then text and textVar and underline ! 57: * are ignored. */ ! 58: ! 59: /* ! 60: * Information used when displaying widget: ! 61: */ ! 62: ! 63: Tk_Uid state; /* State of button for display purposes: ! 64: * normal, active, or disabled. */ ! 65: Tk_3DBorder normalBorder; /* Structure used to draw 3-D ! 66: * border and background when window ! 67: * isn't active. NULL means no such ! 68: * border exists. */ ! 69: Tk_3DBorder activeBorder; /* Structure used to draw 3-D ! 70: * border and background when window ! 71: * is active. NULL means no such ! 72: * border exists. */ ! 73: int borderWidth; /* Width of border. */ ! 74: int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */ ! 75: XFontStruct *fontPtr; /* Information about text font, or NULL. */ ! 76: XColor *normalFg; /* Foreground color in normal mode. */ ! 77: XColor *activeFg; /* Foreground color in active mode. NULL ! 78: * means use normalFg instead. */ ! 79: XColor *disabledFg; /* Foreground color when disabled. NULL ! 80: * means use normalFg with a 50% stipple ! 81: * instead. */ ! 82: GC normalTextGC; /* GC for drawing text in normal mode. */ ! 83: GC activeTextGC; /* GC for drawing text in active mode (NULL ! 84: * means use normalTextGC). */ ! 85: Pixmap gray; /* Pixmap for displaying disabled text/icon if ! 86: * disabledFg is NULL. */ ! 87: GC disabledGC; /* Used to produce disabled effect. If ! 88: * disabledFg isn't NULL, this GC is used to ! 89: * draw button text or icon. Otherwise ! 90: * text or icon is drawn with normalGC and ! 91: * this GC is used to stipple background ! 92: * across it. */ ! 93: int leftBearing; /* Amount text sticks left from its origin, ! 94: * in pixels. */ ! 95: int rightBearing; /* Amount text sticks right from its origin. */ ! 96: int width, height; /* If > 0, these specify dimensions to request ! 97: * for window, in characters for text and in ! 98: * pixels for bitmaps. In this case the actual ! 99: * size of the text string or bitmap is ! 100: * ignored in computing desired window size. */ ! 101: int padX, padY; /* Extra space around text or bitmap (pixels ! 102: * on each side). */ ! 103: Tk_Anchor anchor; /* Where text/bitmap should be displayed ! 104: * inside window region. */ ! 105: ! 106: /* ! 107: * Miscellaneous information: ! 108: */ ! 109: ! 110: Cursor cursor; /* Current cursor for window, or None. */ ! 111: int flags; /* Various flags; see below for ! 112: * definitions. */ ! 113: } MenuButton; ! 114: ! 115: /* ! 116: * Flag bits for buttons: ! 117: * ! 118: * REDRAW_PENDING: Non-zero means a DoWhenIdle handler ! 119: * has already been queued to redraw ! 120: * this window. ! 121: * POSTED: Non-zero means that the menu associated ! 122: * with this button has been posted (typically ! 123: * because of an active button press). ! 124: */ ! 125: ! 126: #define REDRAW_PENDING 1 ! 127: #define POSTED 2 ! 128: ! 129: /* ! 130: * Information used for parsing configuration specs: ! 131: */ ! 132: ! 133: static Tk_ConfigSpec configSpecs[] = { ! 134: {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", ! 135: DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder), ! 136: TK_CONFIG_COLOR_ONLY}, ! 137: {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", ! 138: DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder), ! 139: TK_CONFIG_MONO_ONLY}, ! 140: {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", ! 141: DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg), ! 142: TK_CONFIG_COLOR_ONLY}, ! 143: {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", ! 144: DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg), ! 145: TK_CONFIG_MONO_ONLY}, ! 146: {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", ! 147: DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0}, ! 148: {TK_CONFIG_BORDER, "-background", "background", "Background", ! 149: DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder), ! 150: TK_CONFIG_COLOR_ONLY}, ! 151: {TK_CONFIG_BORDER, "-background", "background", "Background", ! 152: DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder), ! 153: TK_CONFIG_MONO_ONLY}, ! 154: {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, ! 155: (char *) NULL, 0, 0}, ! 156: {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, ! 157: (char *) NULL, 0, 0}, ! 158: #if defined(USE_XPM3) ! 159: {TK_CONFIG_PIXMAP, "-bitmap", "bitmap", "Bitmap", ! 160: DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap), ! 161: TK_CONFIG_NULL_OK}, ! 162: #else ! 163: {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap", ! 164: DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap), ! 165: TK_CONFIG_NULL_OK}, ! 166: #endif ! 167: {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", ! 168: DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0}, ! 169: {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", ! 170: DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor), ! 171: TK_CONFIG_NULL_OK}, ! 172: {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", ! 173: "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR, ! 174: Tk_Offset(MenuButton, disabledFg), ! 175: TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK}, ! 176: {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", ! 177: "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO, ! 178: Tk_Offset(MenuButton, disabledFg), ! 179: TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK}, ! 180: {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, ! 181: (char *) NULL, 0, 0}, ! 182: {TK_CONFIG_FONT, "-font", "font", "Font", ! 183: DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0}, ! 184: {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", ! 185: DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0}, ! 186: {TK_CONFIG_INT, "-height", "height", "Height", ! 187: DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, height), 0}, ! 188: {TK_CONFIG_STRING, "-menu", "menu", "Menu", ! 189: DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName), 0}, ! 190: {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", ! 191: DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0}, ! 192: {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", ! 193: DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0}, ! 194: {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", ! 195: DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0}, ! 196: {TK_CONFIG_UID, "-state", "state", "State", ! 197: DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0}, ! 198: {TK_CONFIG_STRING, "-text", "text", "Text", ! 199: DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0}, ! 200: {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable", ! 201: DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName), ! 202: TK_CONFIG_NULL_OK}, ! 203: {TK_CONFIG_INT, "-underline", "underline", "Underline", ! 204: DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0}, ! 205: {TK_CONFIG_UID, "-variable", "variable", "Variable", ! 206: DEF_MENUBUTTON_VARIABLE, Tk_Offset(MenuButton, varName), 0}, ! 207: {TK_CONFIG_INT, "-width", "width", "Width", ! 208: DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, width), 0}, ! 209: {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, ! 210: (char *) NULL, 0, 0} ! 211: }; ! 212: ! 213: /* ! 214: * Forward declarations for procedures defined later in this file: ! 215: */ ! 216: ! 217: static void ComputeMenuButtonGeometry _ANSI_ARGS_(( ! 218: MenuButton *mbPtr)); ! 219: static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData, ! 220: XEvent *eventPtr)); ! 221: static char * MenuButtonTextVarProc _ANSI_ARGS_(( ! 222: ClientData clientData, Tcl_Interp *interp, ! 223: char *name1, char *name2, int flags)); ! 224: static char * MenuButtonVarProc _ANSI_ARGS_((ClientData clientData, ! 225: Tcl_Interp *interp, char *name1, char *name2, ! 226: int flags)); ! 227: static int MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData, ! 228: Tcl_Interp *interp, int argc, char **argv)); ! 229: static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp, ! 230: MenuButton *mbPtr, int argc, char **argv, ! 231: int flags)); ! 232: static void DestroyMenuButton _ANSI_ARGS_((ClientData clientData)); ! 233: static void DisplayMenuButton _ANSI_ARGS_((ClientData clientData)); ! 234: ! 235: /* ! 236: *-------------------------------------------------------------- ! 237: * ! 238: * Tk_MenubuttonCmd -- ! 239: * ! 240: * This procedure is invoked to process the "button", "label", ! 241: * "radiobutton", and "checkbutton" Tcl commands. See the ! 242: * user documentation for details on what it does. ! 243: * ! 244: * Results: ! 245: * A standard Tcl result. ! 246: * ! 247: * Side effects: ! 248: * See the user documentation. ! 249: * ! 250: *-------------------------------------------------------------- ! 251: */ ! 252: ! 253: int ! 254: Tk_MenubuttonCmd(clientData, interp, argc, argv) ! 255: ClientData clientData; /* Main window associated with ! 256: * interpreter. */ ! 257: Tcl_Interp *interp; /* Current interpreter. */ ! 258: int argc; /* Number of arguments. */ ! 259: char **argv; /* Argument strings. */ ! 260: { ! 261: register MenuButton *mbPtr; ! 262: Tk_Window tkwin = (Tk_Window) clientData; ! 263: Tk_Window new; ! 264: ! 265: if (argc < 2) { ! 266: Tcl_AppendResult(interp, "wrong # args: should be \"", ! 267: argv[0], " pathName ?options?\"", (char *) NULL); ! 268: return TCL_ERROR; ! 269: } ! 270: ! 271: /* ! 272: * Create the new window. ! 273: */ ! 274: ! 275: new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL); ! 276: if (new == NULL) { ! 277: return TCL_ERROR; ! 278: } ! 279: ! 280: /* ! 281: * Initialize the data structure for the button. ! 282: */ ! 283: ! 284: mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton)); ! 285: mbPtr->tkwin = new; ! 286: mbPtr->interp = interp; ! 287: mbPtr->menuName = NULL; ! 288: mbPtr->varName = NULL; ! 289: mbPtr->text = NULL; ! 290: mbPtr->underline = -1; ! 291: mbPtr->textVarName = NULL; ! 292: mbPtr->bitmap = None; ! 293: mbPtr->state = tkNormalUid; ! 294: mbPtr->normalBorder = NULL; ! 295: mbPtr->activeBorder = NULL; ! 296: mbPtr->borderWidth = 0; ! 297: mbPtr->relief = TK_RELIEF_FLAT; ! 298: mbPtr->fontPtr = NULL; ! 299: mbPtr->normalFg = NULL; ! 300: mbPtr->activeFg = NULL; ! 301: mbPtr->disabledFg = NULL; ! 302: mbPtr->normalTextGC = NULL; ! 303: mbPtr->activeTextGC = NULL; ! 304: mbPtr->gray = None; ! 305: mbPtr->disabledGC = NULL; ! 306: mbPtr->cursor = None; ! 307: mbPtr->flags = 0; ! 308: ! 309: Tk_SetClass(mbPtr->tkwin, "Menubutton"); ! 310: Tk_CreateEventHandler(mbPtr->tkwin, ExposureMask|StructureNotifyMask, ! 311: MenuButtonEventProc, (ClientData) mbPtr); ! 312: Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin), MenuButtonWidgetCmd, ! 313: (ClientData) mbPtr, (void (*)()) NULL); ! 314: if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) { ! 315: Tk_DestroyWindow(mbPtr->tkwin); ! 316: return TCL_ERROR; ! 317: } ! 318: ! 319: interp->result = Tk_PathName(mbPtr->tkwin); ! 320: return TCL_OK; ! 321: } ! 322: ! 323: /* ! 324: *-------------------------------------------------------------- ! 325: * ! 326: * MenuButtonWidgetCmd -- ! 327: * ! 328: * This procedure is invoked to process the Tcl command ! 329: * that corresponds to a widget managed by this module. ! 330: * See the user documentation for details on what it does. ! 331: * ! 332: * Results: ! 333: * A standard Tcl result. ! 334: * ! 335: * Side effects: ! 336: * See the user documentation. ! 337: * ! 338: *-------------------------------------------------------------- ! 339: */ ! 340: ! 341: static int ! 342: MenuButtonWidgetCmd(clientData, interp, argc, argv) ! 343: ClientData clientData; /* Information about button widget. */ ! 344: Tcl_Interp *interp; /* Current interpreter. */ ! 345: int argc; /* Number of arguments. */ ! 346: char **argv; /* Argument strings. */ ! 347: { ! 348: register MenuButton *mbPtr = (MenuButton *) clientData; ! 349: int result = TCL_OK; ! 350: int length; ! 351: char c; ! 352: ! 353: if (argc < 2) { ! 354: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], ! 355: " option ?arg arg ...?\"", (char *) NULL); ! 356: return TCL_ERROR; ! 357: } ! 358: Tk_Preserve((ClientData) mbPtr); ! 359: c = argv[1][0]; ! 360: length = strlen(argv[1]); ! 361: if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) { ! 362: if (argc > 2) { ! 363: Tcl_AppendResult(interp, "wrong # args: should be \"", ! 364: argv[0], " activate\"", (char *) NULL); ! 365: goto error; ! 366: } ! 367: if (mbPtr->state != tkDisabledUid) { ! 368: mbPtr->state = tkActiveUid; ! 369: Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder); ! 370: goto redisplay; ! 371: } ! 372: } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { ! 373: if (argc == 2) { ! 374: result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs, ! 375: (char *) mbPtr, (char *) NULL, 0); ! 376: } else if (argc == 3) { ! 377: result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs, ! 378: (char *) mbPtr, argv[2], 0); ! 379: } else { ! 380: result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, ! 381: TK_CONFIG_ARGV_ONLY); ! 382: } ! 383: } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) { ! 384: if (argc > 2) { ! 385: Tcl_AppendResult(interp, "wrong # args: should be \"", ! 386: argv[0], " deactivate\"", (char *) NULL); ! 387: goto error; ! 388: } ! 389: if (mbPtr->state != tkDisabledUid) { ! 390: mbPtr->state = tkNormalUid; ! 391: Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder); ! 392: goto redisplay; ! 393: } ! 394: } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) { ! 395: if (argc > 2) { ! 396: Tcl_AppendResult(interp, "wrong # args: should be \"", ! 397: argv[0], " post\"", (char *) NULL); ! 398: goto error; ! 399: } ! 400: if ((mbPtr->flags & POSTED) || (mbPtr->menuName == NULL) ! 401: || (mbPtr->state == tkDisabledUid)) { ! 402: goto done; ! 403: } ! 404: ! 405: /* ! 406: * Store the name of the posted menu into the associated variable. ! 407: * This will cause any other menu posted via that variable to ! 408: * unpost itself and will cause this menu to post itself. ! 409: */ ! 410: ! 411: Tcl_SetVar(interp, mbPtr->varName, Tk_PathName(mbPtr->tkwin), ! 412: TCL_GLOBAL_ONLY); ! 413: } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) { ! 414: if (argc > 2) { ! 415: Tcl_AppendResult(interp, "wrong # args: should be \"", ! 416: argv[0], " unpost\"", (char *) NULL); ! 417: goto error; ! 418: } ! 419: ! 420: /* ! 421: * The one-liner below looks simple, but it isn't. This code ! 422: * does the right thing even if this menu isn't posted anymore, ! 423: * but some other variable associated with the same variable ! 424: * is posted instead: it unposts whatever is posted. This ! 425: * approach is necessary because at present ButtonRelease ! 426: * events go to the menu button where the mouse button was ! 427: * first pressed; this may not be the same menu button that's ! 428: * currently active. ! 429: */ ! 430: Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY); ! 431: } else { ! 432: Tcl_AppendResult(interp, "bad option \"", argv[1], ! 433: "\": must be activate, configure, deactivate, ", ! 434: "post, or unpost", (char *) NULL); ! 435: goto error; ! 436: } ! 437: done: ! 438: Tk_Release((ClientData) mbPtr); ! 439: return result; ! 440: ! 441: redisplay: ! 442: if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { ! 443: Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); ! 444: mbPtr->flags |= REDRAW_PENDING; ! 445: } ! 446: goto done; ! 447: ! 448: error: ! 449: Tk_Release((ClientData) mbPtr); ! 450: return TCL_ERROR; ! 451: } ! 452: ! 453: /* ! 454: *---------------------------------------------------------------------- ! 455: * ! 456: * DestroyMenuButton -- ! 457: * ! 458: * This procedure is invoked to recycle all of the resources ! 459: * associated with a button widget. It is invoked as a ! 460: * when-idle handler in order to make sure that there is no ! 461: * other use of the button pending at the time of the deletion. ! 462: * ! 463: * Results: ! 464: * None. ! 465: * ! 466: * Side effects: ! 467: * Everything associated with the widget is freed up. ! 468: * ! 469: *---------------------------------------------------------------------- ! 470: */ ! 471: ! 472: static void ! 473: DestroyMenuButton(clientData) ! 474: ClientData clientData; /* Info about button widget. */ ! 475: { ! 476: register MenuButton *mbPtr = (MenuButton *) clientData; ! 477: if (mbPtr->menuName != NULL) { ! 478: ckfree(mbPtr->menuName); ! 479: } ! 480: if (mbPtr->varName != NULL) { ! 481: Tcl_UntraceVar(mbPtr->interp, mbPtr->varName, ! 482: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 483: MenuButtonVarProc, (ClientData) mbPtr); ! 484: } ! 485: if (mbPtr->text != NULL) { ! 486: ckfree(mbPtr->text); ! 487: } ! 488: if (mbPtr->textVarName != NULL) { ! 489: Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName, ! 490: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 491: MenuButtonTextVarProc, (ClientData) mbPtr); ! 492: ckfree(mbPtr->textVarName); ! 493: } ! 494: if (mbPtr->bitmap != None) { ! 495: #if defined(USE_XPM3) ! 496: Tk_FreePixmap(mbPtr->bitmap); ! 497: #else ! 498: Tk_FreeBitmap(mbPtr->bitmap); ! 499: #endif ! 500: } ! 501: if (mbPtr->normalBorder != NULL) { ! 502: Tk_Free3DBorder(mbPtr->normalBorder); ! 503: } ! 504: if (mbPtr->activeBorder != NULL) { ! 505: Tk_Free3DBorder(mbPtr->activeBorder); ! 506: } ! 507: if (mbPtr->fontPtr != NULL) { ! 508: Tk_FreeFontStruct(mbPtr->fontPtr); ! 509: } ! 510: if (mbPtr->normalFg != NULL) { ! 511: Tk_FreeColor(mbPtr->normalFg); ! 512: } ! 513: if (mbPtr->activeFg != NULL) { ! 514: Tk_FreeColor(mbPtr->activeFg); ! 515: } ! 516: if (mbPtr->disabledFg != NULL) { ! 517: Tk_FreeColor(mbPtr->disabledFg); ! 518: } ! 519: if (mbPtr->normalTextGC != None) { ! 520: Tk_FreeGC(mbPtr->normalTextGC); ! 521: } ! 522: if (mbPtr->activeTextGC != None) { ! 523: Tk_FreeGC(mbPtr->activeTextGC); ! 524: } ! 525: if (mbPtr->gray != None) { ! 526: Tk_FreeBitmap(mbPtr->gray); ! 527: } ! 528: if (mbPtr->disabledGC != None) { ! 529: Tk_FreeGC(mbPtr->disabledGC); ! 530: } ! 531: if (mbPtr->cursor != None) { ! 532: Tk_FreeCursor(mbPtr->cursor); ! 533: } ! 534: ckfree((char *) mbPtr); ! 535: } ! 536: ! 537: /* ! 538: *---------------------------------------------------------------------- ! 539: * ! 540: * ConfigureMenuButton -- ! 541: * ! 542: * This procedure is called to process an argv/argc list, plus ! 543: * the Tk option database, in order to configure (or ! 544: * reconfigure) a menubutton widget. ! 545: * ! 546: * Results: ! 547: * The return value is a standard Tcl result. If TCL_ERROR is ! 548: * returned, then interp->result contains an error message. ! 549: * ! 550: * Side effects: ! 551: * Configuration information, such as text string, colors, font, ! 552: * etc. get set for mbPtr; old resources get freed, if there ! 553: * were any. The menubutton is redisplayed. ! 554: * ! 555: *---------------------------------------------------------------------- ! 556: */ ! 557: ! 558: static int ! 559: ConfigureMenuButton(interp, mbPtr, argc, argv, flags) ! 560: Tcl_Interp *interp; /* Used for error reporting. */ ! 561: register MenuButton *mbPtr; /* Information about widget; may or may ! 562: * not already have values for some fields. */ ! 563: int argc; /* Number of valid entries in argv. */ ! 564: char **argv; /* Arguments. */ ! 565: int flags; /* Flags to pass to Tk_ConfigureWidget. */ ! 566: { ! 567: XGCValues gcValues; ! 568: GC newGC; ! 569: unsigned long mask; ! 570: int result; ! 571: Tk_Uid oldGroup; ! 572: char *value; ! 573: ! 574: /* ! 575: * Eliminate any existing traces on variables monitored by the button. ! 576: */ ! 577: ! 578: if (mbPtr->varName != NULL) { ! 579: Tcl_UntraceVar(interp, mbPtr->varName, ! 580: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 581: MenuButtonVarProc, (ClientData) mbPtr); ! 582: } ! 583: if (mbPtr->textVarName != NULL) { ! 584: Tcl_UntraceVar(interp, mbPtr->textVarName, ! 585: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 586: MenuButtonTextVarProc, (ClientData) mbPtr); ! 587: } ! 588: ! 589: oldGroup = mbPtr->varName; ! 590: result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs, ! 591: argc, argv, (char *) mbPtr, flags); ! 592: if (oldGroup != mbPtr->varName) { ! 593: Tk_UnshareEvents(mbPtr->tkwin, oldGroup); ! 594: Tk_ShareEvents(mbPtr->tkwin, mbPtr->varName); ! 595: } ! 596: if (result != TCL_OK) { ! 597: return TCL_ERROR; ! 598: } ! 599: ! 600: /* ! 601: * A few options need special processing, such as setting the ! 602: * background from a 3-D border, or filling in complicated ! 603: * defaults that couldn't be specified to Tk_ConfigureWidget. ! 604: */ ! 605: ! 606: if (mbPtr->state == tkActiveUid) { ! 607: Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder); ! 608: } else { ! 609: Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder); ! 610: if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkDisabledUid)) { ! 611: Tcl_AppendResult(interp, "bad state value \"", mbPtr->state, ! 612: "\": must be normal, active, or disabled", (char *) NULL); ! 613: mbPtr->state = tkNormalUid; ! 614: return TCL_ERROR; ! 615: } ! 616: } ! 617: ! 618: gcValues.font = mbPtr->fontPtr->fid; ! 619: gcValues.foreground = mbPtr->normalFg->pixel; ! 620: gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; ! 621: ! 622: /* ! 623: * Note: GraphicsExpose events are disabled in GC's because they're ! 624: * used to copy stuff from an off-screen pixmap onto the screen (we know ! 625: * that there's no problem with obscured areas). ! 626: */ ! 627: ! 628: gcValues.graphics_exposures = False; ! 629: newGC = Tk_GetGC(mbPtr->tkwin, ! 630: GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues); ! 631: if (mbPtr->normalTextGC != None) { ! 632: Tk_FreeGC(mbPtr->normalTextGC); ! 633: } ! 634: mbPtr->normalTextGC = newGC; ! 635: ! 636: gcValues.font = mbPtr->fontPtr->fid; ! 637: gcValues.foreground = mbPtr->activeFg->pixel; ! 638: gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel; ! 639: newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont, ! 640: &gcValues); ! 641: if (mbPtr->activeTextGC != None) { ! 642: Tk_FreeGC(mbPtr->activeTextGC); ! 643: } ! 644: mbPtr->activeTextGC = newGC; ! 645: ! 646: gcValues.font = mbPtr->fontPtr->fid; ! 647: gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; ! 648: if (mbPtr->disabledFg != NULL) { ! 649: gcValues.foreground = mbPtr->disabledFg->pixel; ! 650: mask = GCForeground|GCBackground|GCFont; ! 651: } else { ! 652: gcValues.foreground = gcValues.background; ! 653: if (mbPtr->gray == None) { ! 654: mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin, ! 655: Tk_GetUid("gray50")); ! 656: if (mbPtr->gray == None) { ! 657: return TCL_ERROR; ! 658: } ! 659: } ! 660: gcValues.fill_style = FillStippled; ! 661: gcValues.stipple = mbPtr->gray; ! 662: mask = GCForeground|GCFillStyle|GCStipple; ! 663: } ! 664: newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); ! 665: if (mbPtr->disabledGC != None) { ! 666: Tk_FreeGC(mbPtr->disabledGC); ! 667: } ! 668: mbPtr->disabledGC = newGC; ! 669: ! 670: if (mbPtr->padX < 0) { ! 671: mbPtr->padX = 0; ! 672: } ! 673: if (mbPtr->padY < 0) { ! 674: mbPtr->padY = 0; ! 675: } ! 676: ! 677: /* ! 678: * Set up a trace on the menu button's variable, then initialize ! 679: * the variable if it doesn't already exist, so that it can be ! 680: * accessed immediately from Tcl code without fear of ! 681: * "nonexistent variable" errors. ! 682: */ ! 683: ! 684: value = Tcl_GetVar(interp, mbPtr->varName, TCL_GLOBAL_ONLY); ! 685: if (value == NULL) { ! 686: Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY); ! 687: } ! 688: Tcl_TraceVar(interp, mbPtr->varName, ! 689: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 690: MenuButtonVarProc, (ClientData) mbPtr); ! 691: ! 692: /* ! 693: * Set up a trace on the variable that determines what's displayed ! 694: * in the menu button, if such a trace has been requested. ! 695: */ ! 696: ! 697: if ((mbPtr->bitmap == None) && (mbPtr->textVarName != NULL)) { ! 698: char *value; ! 699: ! 700: value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY); ! 701: if (value == NULL) { ! 702: Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text, ! 703: TCL_GLOBAL_ONLY); ! 704: } else { ! 705: if (mbPtr->text != NULL) { ! 706: ckfree(mbPtr->text); ! 707: } ! 708: mbPtr->text = ckalloc((unsigned) (strlen(value) + 1)); ! 709: strcpy(mbPtr->text, value); ! 710: } ! 711: Tcl_TraceVar(interp, mbPtr->textVarName, ! 712: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 713: MenuButtonTextVarProc, (ClientData) mbPtr); ! 714: } ! 715: ! 716: /* ! 717: * Recompute the geometry for the button. ! 718: */ ! 719: ! 720: ComputeMenuButtonGeometry(mbPtr); ! 721: ! 722: /* ! 723: * Lastly, arrange for the button to be redisplayed. ! 724: */ ! 725: ! 726: if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { ! 727: Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); ! 728: mbPtr->flags |= REDRAW_PENDING; ! 729: } ! 730: ! 731: return TCL_OK; ! 732: } ! 733: ! 734: /* ! 735: *---------------------------------------------------------------------- ! 736: * ! 737: * DisplayMenuButton -- ! 738: * ! 739: * This procedure is invoked to display a menubutton widget. ! 740: * ! 741: * Results: ! 742: * None. ! 743: * ! 744: * Side effects: ! 745: * Commands are output to X to display the menubutton in its ! 746: * current mode. ! 747: * ! 748: *---------------------------------------------------------------------- ! 749: */ ! 750: ! 751: static void ! 752: DisplayMenuButton(clientData) ! 753: ClientData clientData; /* Information about widget. */ ! 754: { ! 755: register MenuButton *mbPtr = (MenuButton *) clientData; ! 756: GC gc; ! 757: Tk_3DBorder border; ! 758: Pixmap pixmap; ! 759: int x = 0; /* Initialization needed only to stop ! 760: * compiler warning. */ ! 761: int y; ! 762: register Tk_Window tkwin = mbPtr->tkwin; ! 763: ! 764: mbPtr->flags &= ~REDRAW_PENDING; ! 765: if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { ! 766: return; ! 767: } ! 768: ! 769: if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) { ! 770: gc = mbPtr->disabledGC; ! 771: border = mbPtr->normalBorder; ! 772: } else if (mbPtr->state == tkActiveUid) { ! 773: gc = mbPtr->activeTextGC; ! 774: border = mbPtr->activeBorder; ! 775: } else { ! 776: gc = mbPtr->normalTextGC; ! 777: border = mbPtr->normalBorder; ! 778: } ! 779: ! 780: /* ! 781: * In order to avoid screen flashes, this procedure redraws ! 782: * the menu button in a pixmap, then copies the pixmap to the ! 783: * screen in a single operation. This means that there's no ! 784: * point in time where the on-sreen image has been cleared. ! 785: */ ! 786: ! 787: pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), ! 788: Tk_Width(tkwin), Tk_Height(tkwin), ! 789: Tk_DefaultDepth(Tk_Screen(tkwin))); ! 790: Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border, ! 791: 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); ! 792: ! 793: /* ! 794: * Display bitmap or text for button. ! 795: */ ! 796: ! 797: if (mbPtr->bitmap != None) { ! 798: unsigned int width, height; ! 799: ! 800: #if defined(USE_XPM3) ! 801: Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height); ! 802: #else ! 803: Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height); ! 804: #endif ! 805: switch (mbPtr->anchor) { ! 806: case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW: ! 807: x += mbPtr->borderWidth + mbPtr->padX; ! 808: break; ! 809: case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S: ! 810: x += (Tk_Width(tkwin) - width)/2; ! 811: break; ! 812: default: ! 813: x += Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX ! 814: - width; ! 815: break; ! 816: } ! 817: switch (mbPtr->anchor) { ! 818: case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE: ! 819: y = mbPtr->borderWidth + mbPtr->padY; ! 820: break; ! 821: case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E: ! 822: y = (Tk_Height(tkwin) - height)/2; ! 823: break; ! 824: default: ! 825: y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY ! 826: - height; ! 827: break; ! 828: } ! 829: #if defined(USE_XPM3) ! 830: XCopyArea(Tk_Display(tkwin), mbPtr->bitmap, pixmap, ! 831: gc, 0, 0, width, height, x, y); ! 832: #else ! 833: XCopyPlane(Tk_Display(tkwin), mbPtr->bitmap, pixmap, ! 834: gc, 0, 0, width, height, x, y, 1); ! 835: #endif ! 836: } else { ! 837: switch (mbPtr->anchor) { ! 838: case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW: ! 839: x = mbPtr->borderWidth + mbPtr->padX + mbPtr->leftBearing; ! 840: break; ! 841: case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S: ! 842: x = (Tk_Width(tkwin) + mbPtr->leftBearing ! 843: - mbPtr->rightBearing)/2; ! 844: break; ! 845: default: ! 846: x = Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX ! 847: - mbPtr->rightBearing; ! 848: break; ! 849: } ! 850: switch (mbPtr->anchor) { ! 851: case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE: ! 852: y = mbPtr->borderWidth + mbPtr->fontPtr->ascent ! 853: + mbPtr->padY; ! 854: break; ! 855: case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E: ! 856: y = (Tk_Height(tkwin) + mbPtr->fontPtr->ascent ! 857: - mbPtr->fontPtr->descent)/2; ! 858: break; ! 859: default: ! 860: y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY ! 861: - mbPtr->fontPtr->descent; ! 862: break; ! 863: } ! 864: XDrawString(Tk_Display(tkwin), pixmap, gc, x, y, mbPtr->text, ! 865: mbPtr->textLength); ! 866: if (mbPtr->underline >= 0) { ! 867: TkUnderlineChars(Tk_Display(tkwin), pixmap, gc, mbPtr->fontPtr, ! 868: mbPtr->text, x, y, TK_NEWLINES_NOT_SPECIAL, ! 869: mbPtr->underline, mbPtr->underline); ! 870: } ! 871: } ! 872: ! 873: /* ! 874: * If the menu button is disabled with a stipple rather than a special ! 875: * foreground color, generate the stippled effect. ! 876: */ ! 877: ! 878: if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg == NULL)) { ! 879: XFillRectangle(Tk_Display(tkwin), pixmap, mbPtr->disabledGC, ! 880: mbPtr->borderWidth, mbPtr->borderWidth, ! 881: (unsigned) (Tk_Width(tkwin) - 2*mbPtr->borderWidth), ! 882: (unsigned) (Tk_Height(tkwin) - 2*mbPtr->borderWidth)); ! 883: } ! 884: ! 885: /* ! 886: * Draw the border last. This way, if the menu button's contents ! 887: * overflow onto the border they'll be covered up by the border. ! 888: */ ! 889: ! 890: if (mbPtr->relief != TK_RELIEF_FLAT) { ! 891: Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border, ! 892: 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), ! 893: mbPtr->borderWidth, mbPtr->relief); ! 894: } ! 895: ! 896: /* ! 897: * Copy the information from the off-screen pixmap onto the screen, ! 898: * then delete the pixmap. ! 899: */ ! 900: ! 901: XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), ! 902: mbPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); ! 903: XFreePixmap(Tk_Display(tkwin), pixmap); ! 904: } ! 905: ! 906: /* ! 907: *-------------------------------------------------------------- ! 908: * ! 909: * MenuButtonEventProc -- ! 910: * ! 911: * This procedure is invoked by the Tk dispatcher for various ! 912: * events on buttons. ! 913: * ! 914: * Results: ! 915: * None. ! 916: * ! 917: * Side effects: ! 918: * When the window gets deleted, internal structures get ! 919: * cleaned up. When it gets exposed, it is redisplayed. ! 920: * ! 921: *-------------------------------------------------------------- ! 922: */ ! 923: ! 924: static void ! 925: MenuButtonEventProc(clientData, eventPtr) ! 926: ClientData clientData; /* Information about window. */ ! 927: XEvent *eventPtr; /* Information about event. */ ! 928: { ! 929: MenuButton *mbPtr = (MenuButton *) clientData; ! 930: if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { ! 931: if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) { ! 932: Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); ! 933: mbPtr->flags |= REDRAW_PENDING; ! 934: } ! 935: } else if (eventPtr->type == DestroyNotify) { ! 936: Tcl_DeleteCommand(mbPtr->interp, Tk_PathName(mbPtr->tkwin)); ! 937: ! 938: /* ! 939: * Careful! Must delete the event-sharing information here ! 940: * rather than in DestroyMenuButton. By the time that procedure ! 941: * is called the tkwin may have been reused, resulting in some ! 942: * other window accidentally being cut off from shared events. ! 943: */ ! 944: ! 945: Tk_UnshareEvents(mbPtr->tkwin, mbPtr->varName); ! 946: mbPtr->tkwin = NULL; ! 947: if (mbPtr->flags & REDRAW_PENDING) { ! 948: Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr); ! 949: } ! 950: Tk_EventuallyFree((ClientData) mbPtr, DestroyMenuButton); ! 951: } ! 952: } ! 953: ! 954: /* ! 955: *---------------------------------------------------------------------- ! 956: * ! 957: * ComputeMenuButtonGeometry -- ! 958: * ! 959: * After changes in a menu button's text or bitmap, this procedure ! 960: * recomputes the menu button's geometry and passes this information ! 961: * along to the geometry manager for the window. ! 962: * ! 963: * Results: ! 964: * None. ! 965: * ! 966: * Side effects: ! 967: * The menu button's window may change size. ! 968: * ! 969: *---------------------------------------------------------------------- ! 970: */ ! 971: ! 972: static void ! 973: ComputeMenuButtonGeometry(mbPtr) ! 974: register MenuButton *mbPtr; /* Widget record for menu button. */ ! 975: { ! 976: XCharStruct bbox; ! 977: int dummy; ! 978: unsigned int width, height; ! 979: ! 980: if (mbPtr->bitmap != None) { ! 981: #if defined(USE_XPM3) ! 982: Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height); ! 983: #else ! 984: Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height); ! 985: #endif ! 986: if (mbPtr->width > 0) { ! 987: width = mbPtr->width; ! 988: } ! 989: if (mbPtr->height > 0) { ! 990: height = mbPtr->height; ! 991: } ! 992: } else { ! 993: mbPtr->textLength = strlen(mbPtr->text); ! 994: XTextExtents(mbPtr->fontPtr, mbPtr->text, mbPtr->textLength, ! 995: &dummy, &dummy, &dummy, &bbox); ! 996: mbPtr->leftBearing = bbox.lbearing; ! 997: mbPtr->rightBearing = bbox.rbearing; ! 998: width = bbox.lbearing + bbox.rbearing; ! 999: height = mbPtr->fontPtr->ascent + mbPtr->fontPtr->descent; ! 1000: if (mbPtr->width > 0) { ! 1001: width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1); ! 1002: } ! 1003: if (mbPtr->height > 0) { ! 1004: height *= mbPtr->height; ! 1005: } ! 1006: } ! 1007: ! 1008: width += 2*mbPtr->padX; ! 1009: height += 2*mbPtr->padY; ! 1010: Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->borderWidth), ! 1011: (int) (height + 2*mbPtr->borderWidth)); ! 1012: Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->borderWidth); ! 1013: } ! 1014: ! 1015: /* ! 1016: *-------------------------------------------------------------- ! 1017: * ! 1018: * MenuButtonVarProc -- ! 1019: * ! 1020: * This procedure is invoked when someone changes the ! 1021: * state variable associated with a menubutton. This causes ! 1022: * the posted/unposted state of the menu to change if needed ! 1023: * to match the variable's new value. ! 1024: * ! 1025: * Results: ! 1026: * NULL is always returned. ! 1027: * ! 1028: * Side effects: ! 1029: * The menu may be posted or unposted. ! 1030: * ! 1031: *-------------------------------------------------------------- ! 1032: */ ! 1033: ! 1034: /* ARGSUSED */ ! 1035: static char * ! 1036: MenuButtonVarProc(clientData, interp, name1, name2, flags) ! 1037: ClientData clientData; /* Information about button. */ ! 1038: Tcl_Interp *interp; /* Interpreter containing variable. */ ! 1039: char *name1; /* First part of variable's name. */ ! 1040: char *name2; /* Second part of variable's name. */ ! 1041: int flags; /* Describes what's happening to variable. */ ! 1042: { ! 1043: register MenuButton *mbPtr = (MenuButton *) clientData; ! 1044: char *value; ! 1045: int newFlags; ! 1046: ! 1047: /* ! 1048: * If the variable is being unset, then just re-establish the ! 1049: * trace unless the whole interpreter is going away. Also unpost ! 1050: * the menu. ! 1051: */ ! 1052: ! 1053: newFlags = mbPtr->flags; ! 1054: if (flags & TCL_TRACE_UNSETS) { ! 1055: newFlags &= ~POSTED; ! 1056: if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { ! 1057: Tcl_TraceVar2(interp, name1, name2, ! 1058: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 1059: MenuButtonVarProc, clientData); ! 1060: } ! 1061: } else { ! 1062: ! 1063: /* ! 1064: * Use the value of the variable to update the posted status of ! 1065: * the menu. ! 1066: */ ! 1067: ! 1068: value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); ! 1069: if (strcmp(value, Tk_PathName(mbPtr->tkwin)) == 0) { ! 1070: newFlags |= POSTED; ! 1071: } else { ! 1072: newFlags &= ~POSTED; ! 1073: } ! 1074: } ! 1075: ! 1076: if ((mbPtr->menuName != NULL) && (newFlags != mbPtr->flags)) { ! 1077: mbPtr->flags = newFlags; ! 1078: if (newFlags & POSTED) { ! 1079: int x, y; ! 1080: char string[50]; ! 1081: ! 1082: /* ! 1083: * Post the menu just below the menu button. ! 1084: */ ! 1085: ! 1086: Tk_GetRootCoords(mbPtr->tkwin, &x, &y); ! 1087: y += Tk_Height(mbPtr->tkwin); ! 1088: sprintf(string, "%d %d ", x, y); ! 1089: if (Tcl_VarEval(interp, mbPtr->menuName, " post ", string, ! 1090: mbPtr->varName, (char *) NULL) != TCL_OK) { ! 1091: TkBindError(interp); ! 1092: } ! 1093: } else { ! 1094: if (Tcl_VarEval(interp, mbPtr->menuName, " unpost", ! 1095: (char *) NULL) != TCL_OK) { ! 1096: TkBindError(interp); ! 1097: } ! 1098: } ! 1099: } ! 1100: return (char *) NULL; ! 1101: } ! 1102: ! 1103: /* ! 1104: *-------------------------------------------------------------- ! 1105: * ! 1106: * MenuButtonTextVarProc -- ! 1107: * ! 1108: * This procedure is invoked when someone changes the variable ! 1109: * whose contents are to be displayed in a menu button. ! 1110: * ! 1111: * Results: ! 1112: * NULL is always returned. ! 1113: * ! 1114: * Side effects: ! 1115: * The text displayed in the menu button will change to match the ! 1116: * variable. ! 1117: * ! 1118: *-------------------------------------------------------------- ! 1119: */ ! 1120: ! 1121: /* ARGSUSED */ ! 1122: static char * ! 1123: MenuButtonTextVarProc(clientData, interp, name1, name2, flags) ! 1124: ClientData clientData; /* Information about button. */ ! 1125: Tcl_Interp *interp; /* Interpreter containing variable. */ ! 1126: char *name1; /* Name of variable. */ ! 1127: char *name2; /* Second part of variable name. */ ! 1128: int flags; /* Information about what happened. */ ! 1129: { ! 1130: register MenuButton *mbPtr = (MenuButton *) clientData; ! 1131: char *value; ! 1132: ! 1133: /* ! 1134: * If the variable is unset, then immediately recreate it unless ! 1135: * the whole interpreter is going away. ! 1136: */ ! 1137: ! 1138: if (flags & TCL_TRACE_UNSETS) { ! 1139: if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { ! 1140: Tcl_SetVar2(interp, name1, name2, mbPtr->text, ! 1141: flags & TCL_GLOBAL_ONLY); ! 1142: Tcl_TraceVar2(interp, name1, name2, ! 1143: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, ! 1144: MenuButtonTextVarProc, clientData); ! 1145: } ! 1146: return (char *) NULL; ! 1147: } ! 1148: ! 1149: value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); ! 1150: if (value == NULL) { ! 1151: value = ""; ! 1152: } ! 1153: if (mbPtr->text != NULL) { ! 1154: ckfree(mbPtr->text); ! 1155: } ! 1156: mbPtr->text = ckalloc((unsigned) (strlen(value) + 1)); ! 1157: strcpy(mbPtr->text, value); ! 1158: ComputeMenuButtonGeometry(mbPtr); ! 1159: ! 1160: if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin) ! 1161: && !(mbPtr->flags & REDRAW_PENDING)) { ! 1162: Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr); ! 1163: mbPtr->flags |= REDRAW_PENDING; ! 1164: } ! 1165: return (char *) NULL; ! 1166: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.