1/* 2 * tkMenubutton.c -- 3 * 4 * This module implements button-like widgets that are used to invoke 5 * pull-down menus. 6 * 7 * Copyright (c) 1990-1994 The Regents of the University of California. 8 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 9 * 10 * See the file "license.terms" for information on usage and redistribution of 11 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id$ 14 */ 15 16#include "tkInt.h" 17#include "tkMenubutton.h" 18#include "default.h" 19 20/* 21 * The following table defines the legal values for the -direction option. It 22 * is used together with the "enum direction" declaration in tkMenubutton.h. 23 */ 24 25static char *directionStrings[] = { 26 "above", "below", "flush", "left", "right", NULL 27}; 28 29/* 30 * The following table defines the legal values for the -state option. It is 31 * used together with the "enum state" declaration in tkMenubutton.h. 32 */ 33 34static char *stateStrings[] = { 35 "active", "disabled", "normal", NULL 36}; 37 38/* 39 * The following table defines the legal values for the -compound option. It 40 * is used with the "enum compound" declaration in tkMenuButton.h 41 */ 42 43static char *compoundStrings[] = { 44 "bottom", "center", "left", "none", "right", "top", NULL 45}; 46 47/* 48 * Information used for parsing configuration specs: 49 */ 50 51static const Tk_OptionSpec optionSpecs[] = { 52 {TK_OPTION_BORDER, "-activebackground", "activeBackground", "Foreground", 53 DEF_MENUBUTTON_ACTIVE_BG_COLOR, -1, 54 Tk_Offset(TkMenuButton, activeBorder), 0, 55 (ClientData) DEF_MENUBUTTON_ACTIVE_BG_MONO, 0}, 56 {TK_OPTION_COLOR, "-activeforeground", "activeForeground", "Background", 57 DEF_MENUBUTTON_ACTIVE_FG_COLOR, -1, 58 Tk_Offset(TkMenuButton, activeFg), 59 0, (ClientData) DEF_MENUBUTTON_ACTIVE_FG_MONO, 0}, 60 {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", 61 DEF_MENUBUTTON_ANCHOR, -1, 62 Tk_Offset(TkMenuButton, anchor), 0, 0, 0}, 63 {TK_OPTION_BORDER, "-background", "background", "Background", 64 DEF_MENUBUTTON_BG_COLOR, -1, Tk_Offset(TkMenuButton, normalBorder), 65 0, (ClientData) DEF_MENUBUTTON_BG_MONO, 0}, 66 {TK_OPTION_SYNONYM, "-bd", NULL, NULL, NULL, 0, -1, 0, 67 (ClientData) "-borderwidth", 0}, 68 {TK_OPTION_SYNONYM, "-bg", NULL, NULL, NULL, 0, -1, 0, 69 (ClientData) "-background", 0}, 70 {TK_OPTION_BITMAP, "-bitmap", "bitmap", "Bitmap", 71 DEF_MENUBUTTON_BITMAP, -1, Tk_Offset(TkMenuButton, bitmap), 72 TK_OPTION_NULL_OK, 0, 0}, 73 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 74 DEF_MENUBUTTON_BORDER_WIDTH, -1, 75 Tk_Offset(TkMenuButton, borderWidth), 0, 0, 0}, 76 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", 77 DEF_MENUBUTTON_CURSOR, -1, Tk_Offset(TkMenuButton, cursor), 78 TK_OPTION_NULL_OK, 0, 0}, 79 {TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction", 80 DEF_MENUBUTTON_DIRECTION, -1, Tk_Offset(TkMenuButton, direction), 81 0, (ClientData) directionStrings, 0}, 82 {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground", 83 "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR, 84 -1, Tk_Offset(TkMenuButton, disabledFg), TK_OPTION_NULL_OK, 85 (ClientData) DEF_MENUBUTTON_DISABLED_FG_MONO, 0}, 86 {TK_OPTION_SYNONYM, "-fg", "foreground", NULL, NULL, 0, -1, 0, 87 (ClientData) "-foreground", 0}, 88 {TK_OPTION_FONT, "-font", "font", "Font", 89 DEF_MENUBUTTON_FONT, -1, Tk_Offset(TkMenuButton, tkfont), 0, 0, 0}, 90 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", 91 DEF_MENUBUTTON_FG, -1, Tk_Offset(TkMenuButton, normalFg), 0, 0, 0}, 92 {TK_OPTION_STRING, "-height", "height", "Height", 93 DEF_MENUBUTTON_HEIGHT, -1, Tk_Offset(TkMenuButton, heightString), 94 0, 0, 0}, 95 {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", 96 "HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR, 97 -1, Tk_Offset(TkMenuButton, highlightBgColorPtr), 0, 0, 0}, 98 {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", 99 DEF_MENUBUTTON_HIGHLIGHT, -1, 100 Tk_Offset(TkMenuButton, highlightColorPtr), 0, 0, 0}, 101 {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", 102 "HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH, 103 -1, Tk_Offset(TkMenuButton, highlightWidth), 0, 0, 0}, 104 {TK_OPTION_STRING, "-image", "image", "Image", 105 DEF_MENUBUTTON_IMAGE, -1, Tk_Offset(TkMenuButton, imageString), 106 TK_OPTION_NULL_OK, 0, 0}, 107 {TK_OPTION_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn", 108 DEF_MENUBUTTON_INDICATOR, -1, Tk_Offset(TkMenuButton, indicatorOn), 109 0, 0, 0}, 110 {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", 111 DEF_MENUBUTTON_JUSTIFY, -1, Tk_Offset(TkMenuButton, justify), 0, 0, 0}, 112 {TK_OPTION_STRING, "-menu", "menu", "Menu", 113 DEF_MENUBUTTON_MENU, -1, Tk_Offset(TkMenuButton, menuName), 114 TK_OPTION_NULL_OK, 0, 0}, 115 {TK_OPTION_PIXELS, "-padx", "padX", "Pad", 116 DEF_MENUBUTTON_PADX, -1, Tk_Offset(TkMenuButton, padX), 117 0, 0, 0}, 118 {TK_OPTION_PIXELS, "-pady", "padY", "Pad", 119 DEF_MENUBUTTON_PADY, -1, Tk_Offset(TkMenuButton, padY), 120 0, 0, 0}, 121 {TK_OPTION_RELIEF, "-relief", "relief", "Relief", 122 DEF_MENUBUTTON_RELIEF, -1, Tk_Offset(TkMenuButton, relief), 123 0, 0, 0}, 124 {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound", 125 DEF_BUTTON_COMPOUND, -1, Tk_Offset(TkMenuButton, compound), 0, 126 (ClientData) compoundStrings, 0}, 127 {TK_OPTION_STRING_TABLE, "-state", "state", "State", 128 DEF_MENUBUTTON_STATE, -1, Tk_Offset(TkMenuButton, state), 129 0, (ClientData) stateStrings, 0}, 130 {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", 131 DEF_MENUBUTTON_TAKE_FOCUS, -1, 132 Tk_Offset(TkMenuButton, takeFocus), TK_OPTION_NULL_OK, 0, 0}, 133 {TK_OPTION_STRING, "-text", "text", "Text", 134 DEF_MENUBUTTON_TEXT, -1, Tk_Offset(TkMenuButton, text), 0, 0, 0}, 135 {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", 136 DEF_MENUBUTTON_TEXT_VARIABLE, -1, 137 Tk_Offset(TkMenuButton, textVarName), TK_OPTION_NULL_OK, 0, 0}, 138 {TK_OPTION_INT, "-underline", "underline", "Underline", 139 DEF_MENUBUTTON_UNDERLINE, -1, Tk_Offset(TkMenuButton, underline), 140 0, 0, 0}, 141 {TK_OPTION_STRING, "-width", "width", "Width", 142 DEF_MENUBUTTON_WIDTH, -1, Tk_Offset(TkMenuButton, widthString), 143 0, 0, 0}, 144 {TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength", 145 DEF_MENUBUTTON_WRAP_LENGTH, -1, Tk_Offset(TkMenuButton, wrapLength), 146 0, 0, 0}, 147 {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0} 148}; 149 150/* 151 * The following tables define the menubutton widget commands and map the 152 * indexes into the string tables into a single enumerated type used to 153 * dispatch the scale widget command. 154 */ 155 156static CONST char *commandNames[] = { 157 "cget", "configure", NULL 158}; 159 160enum command { 161 COMMAND_CGET, COMMAND_CONFIGURE 162}; 163 164/* 165 * Forward declarations for functions defined later in this file: 166 */ 167 168static void MenuButtonCmdDeletedProc(ClientData clientData); 169static void MenuButtonEventProc(ClientData clientData, 170 XEvent *eventPtr); 171static void MenuButtonImageProc(ClientData clientData, 172 int x, int y, int width, int height, int imgWidth, 173 int imgHeight); 174static char * MenuButtonTextVarProc(ClientData clientData, 175 Tcl_Interp *interp, CONST char *name1, 176 CONST char *name2, int flags); 177static int MenuButtonWidgetObjCmd(ClientData clientData, 178 Tcl_Interp *interp, int objc, 179 Tcl_Obj *CONST objv[]); 180static int ConfigureMenuButton(Tcl_Interp *interp, 181 TkMenuButton *mbPtr, int objc, 182 Tcl_Obj *CONST objv[]); 183static void DestroyMenuButton(char *memPtr); 184 185/* 186 *-------------------------------------------------------------- 187 * 188 * Tk_MenubuttonObjCmd -- 189 * 190 * This function is invoked to process the "button", "label", 191 * "radiobutton", and "checkbutton" Tcl commands. See the user 192 * documentation for details on what it does. 193 * 194 * Results: 195 * A standard Tcl result. 196 * 197 * Side effects: 198 * See the user documentation. 199 * 200 *-------------------------------------------------------------- 201 */ 202 203int 204Tk_MenubuttonObjCmd( 205 ClientData clientData, /* NULL. */ 206 Tcl_Interp *interp, /* Current interpreter. */ 207 int objc, /* Number of arguments. */ 208 Tcl_Obj *CONST objv[]) /* Argument objects. */ 209{ 210 register TkMenuButton *mbPtr; 211 Tk_OptionTable optionTable; 212 Tk_Window tkwin; 213 214 if (objc < 2) { 215 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); 216 return TCL_ERROR; 217 } 218 219 /* 220 * Create the new window. 221 */ 222 223 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), 224 Tcl_GetString(objv[1]), NULL); 225 if (tkwin == NULL) { 226 return TCL_ERROR; 227 } 228 229 /* 230 * Create the option table for this widget class. If it has already been 231 * created, the cached pointer will be returned. 232 */ 233 234 optionTable = Tk_CreateOptionTable(interp, optionSpecs); 235 236 Tk_SetClass(tkwin, "Menubutton"); 237 mbPtr = TkpCreateMenuButton(tkwin); 238 239 Tk_SetClassProcs(tkwin, &tkpMenubuttonClass, (ClientData) mbPtr); 240 241 /* 242 * Initialize the data structure for the button. 243 */ 244 245 mbPtr->tkwin = tkwin; 246 mbPtr->display = Tk_Display (tkwin); 247 mbPtr->interp = interp; 248 mbPtr->widgetCmd = Tcl_CreateObjCommand(interp, 249 Tk_PathName(mbPtr->tkwin), MenuButtonWidgetObjCmd, 250 (ClientData) mbPtr, MenuButtonCmdDeletedProc); 251 mbPtr->optionTable = optionTable; 252 mbPtr->menuName = NULL; 253 mbPtr->text = NULL; 254 mbPtr->underline = -1; 255 mbPtr->textVarName = NULL; 256 mbPtr->bitmap = None; 257 mbPtr->imageString = NULL; 258 mbPtr->image = NULL; 259 mbPtr->state = STATE_NORMAL; 260 mbPtr->normalBorder = NULL; 261 mbPtr->activeBorder = NULL; 262 mbPtr->borderWidth = 0; 263 mbPtr->relief = TK_RELIEF_FLAT; 264 mbPtr->highlightWidth = 0; 265 mbPtr->highlightBgColorPtr = NULL; 266 mbPtr->highlightColorPtr = NULL; 267 mbPtr->inset = 0; 268 mbPtr->tkfont = NULL; 269 mbPtr->normalFg = NULL; 270 mbPtr->activeFg = NULL; 271 mbPtr->disabledFg = NULL; 272 mbPtr->normalTextGC = None; 273 mbPtr->activeTextGC = None; 274 mbPtr->gray = None; 275 mbPtr->disabledGC = None; 276 mbPtr->stippleGC = None; 277 mbPtr->leftBearing = 0; 278 mbPtr->rightBearing = 0; 279 mbPtr->widthString = NULL; 280 mbPtr->heightString = NULL; 281 mbPtr->width = 0; 282 mbPtr->width = 0; 283 mbPtr->wrapLength = 0; 284 mbPtr->padX = 0; 285 mbPtr->padY = 0; 286 mbPtr->anchor = TK_ANCHOR_CENTER; 287 mbPtr->justify = TK_JUSTIFY_CENTER; 288 mbPtr->textLayout = NULL; 289 mbPtr->indicatorOn = 0; 290 mbPtr->indicatorWidth = 0; 291 mbPtr->indicatorHeight = 0; 292 mbPtr->direction = DIRECTION_FLUSH; 293 mbPtr->cursor = None; 294 mbPtr->takeFocus = NULL; 295 mbPtr->flags = 0; 296 297 Tk_CreateEventHandler(mbPtr->tkwin, 298 ExposureMask|StructureNotifyMask|FocusChangeMask, 299 MenuButtonEventProc, (ClientData) mbPtr); 300 301 if (Tk_InitOptions(interp, (char *) mbPtr, optionTable, tkwin) != TCL_OK) { 302 Tk_DestroyWindow(mbPtr->tkwin); 303 return TCL_ERROR; 304 } 305 306 if (ConfigureMenuButton(interp, mbPtr, objc-2, objv+2) != TCL_OK) { 307 Tk_DestroyWindow(mbPtr->tkwin); 308 return TCL_ERROR; 309 } 310 311 Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(mbPtr->tkwin), -1); 312 return TCL_OK; 313} 314 315/* 316 *-------------------------------------------------------------- 317 * 318 * MenuButtonWidgetObjCmd -- 319 * 320 * This function is invoked to process the Tcl command that corresponds 321 * to a widget managed by this module. See the user documentation for 322 * details on what it does. 323 * 324 * Results: 325 * A standard Tcl result. 326 * 327 * Side effects: 328 * See the user documentation. 329 * 330 *-------------------------------------------------------------- 331 */ 332 333static int 334MenuButtonWidgetObjCmd( 335 ClientData clientData, /* Information about button widget. */ 336 Tcl_Interp *interp, /* Current interpreter. */ 337 int objc, /* Number of arguments. */ 338 Tcl_Obj *CONST objv[]) /* Argument objects. */ 339{ 340 register TkMenuButton *mbPtr = (TkMenuButton *) clientData; 341 int result, index; 342 Tcl_Obj *objPtr; 343 344 if (objc < 2) { 345 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 346 return TCL_ERROR; 347 } 348 result = Tcl_GetIndexFromObj(interp, objv[1], commandNames, "option", 0, 349 &index); 350 if (result != TCL_OK) { 351 return result; 352 } 353 Tcl_Preserve((ClientData) mbPtr); 354 355 switch (index) { 356 case COMMAND_CGET: 357 if (objc != 3) { 358 Tcl_WrongNumArgs(interp, 1, objv, "cget option"); 359 goto error; 360 } 361 362 objPtr = Tk_GetOptionValue(interp, (char *) mbPtr, 363 mbPtr->optionTable, objv[2], mbPtr->tkwin); 364 if (objPtr == NULL) { 365 goto error; 366 } else { 367 Tcl_SetObjResult(interp, objPtr); 368 } 369 break; 370 371 case COMMAND_CONFIGURE: 372 if (objc <= 3) { 373 objPtr = Tk_GetOptionInfo(interp, (char *) mbPtr, 374 mbPtr->optionTable, (objc == 3) ? objv[2] : NULL, 375 mbPtr->tkwin); 376 if (objPtr == NULL) { 377 goto error; 378 } else { 379 Tcl_SetObjResult(interp, objPtr); 380 } 381 } else { 382 result = ConfigureMenuButton(interp, mbPtr, objc-2, objv+2); 383 } 384 break; 385 } 386 Tcl_Release((ClientData) mbPtr); 387 return result; 388 389 error: 390 Tcl_Release((ClientData) mbPtr); 391 return TCL_ERROR; 392} 393 394/* 395 *---------------------------------------------------------------------- 396 * 397 * DestroyMenuButton -- 398 * 399 * This function is invoked to recycle all of the resources associated 400 * with a menubutton widget. It is invoked as a when-idle handler in 401 * order to make sure that there is no other use of the menubutton 402 * pending at the time of the deletion. 403 * 404 * Results: 405 * None. 406 * 407 * Side effects: 408 * Everything associated with the widget is freed up. 409 * 410 *---------------------------------------------------------------------- 411 */ 412 413static void 414DestroyMenuButton( 415 char *memPtr) /* Info about button widget. */ 416{ 417 register TkMenuButton *mbPtr = (TkMenuButton *) memPtr; 418 TkpDestroyMenuButton(mbPtr); 419 420 if (mbPtr->flags & REDRAW_PENDING) { 421 Tcl_CancelIdleCall(TkpDisplayMenuButton, (ClientData) mbPtr); 422 } 423 424 /* 425 * Free up all the stuff that requires special handling, then let 426 * Tk_FreeOptions handle all the standard option-related stuff. 427 */ 428 429 Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd); 430 if (mbPtr->textVarName != NULL) { 431 Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName, 432 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 433 MenuButtonTextVarProc, (ClientData) mbPtr); 434 } 435 if (mbPtr->image != NULL) { 436 Tk_FreeImage(mbPtr->image); 437 } 438 if (mbPtr->normalTextGC != None) { 439 Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC); 440 } 441 if (mbPtr->activeTextGC != None) { 442 Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC); 443 } 444 if (mbPtr->disabledGC != None) { 445 Tk_FreeGC(mbPtr->display, mbPtr->disabledGC); 446 } 447 if (mbPtr->stippleGC != None) { 448 Tk_FreeGC(mbPtr->display, mbPtr->stippleGC); 449 } 450 if (mbPtr->gray != None) { 451 Tk_FreeBitmap(mbPtr->display, mbPtr->gray); 452 } 453 if (mbPtr->textLayout != NULL) { 454 Tk_FreeTextLayout(mbPtr->textLayout); 455 } 456 Tk_FreeConfigOptions((char *) mbPtr, mbPtr->optionTable, mbPtr->tkwin); 457 mbPtr->tkwin = NULL; 458 Tcl_EventuallyFree((ClientData) mbPtr, TCL_DYNAMIC); 459} 460 461/* 462 *---------------------------------------------------------------------- 463 * 464 * ConfigureMenuButton -- 465 * 466 * This function is called to process an argv/argc list, plus the Tk 467 * option database, in order to configure (or reconfigure) a menubutton 468 * widget. 469 * 470 * Results: 471 * The return value is a standard Tcl result. If TCL_ERROR is returned, 472 * then the interp's result contains an error message. 473 * 474 * Side effects: 475 * Configuration information, such as text string, colors, font, etc. get 476 * set for mbPtr; old resources get freed, if there were any. The 477 * menubutton is redisplayed. 478 * 479 *---------------------------------------------------------------------- 480 */ 481 482static int 483ConfigureMenuButton( 484 Tcl_Interp *interp, /* Used for error reporting. */ 485 register TkMenuButton *mbPtr, 486 /* Information about widget; may or may not 487 * already have values for some fields. */ 488 int objc, /* Number of valid entries in objv. */ 489 Tcl_Obj *CONST objv[]) /* Arguments. */ 490{ 491 Tk_SavedOptions savedOptions; 492 Tcl_Obj *errorResult = NULL; 493 int error; 494 Tk_Image image; 495 496 /* 497 * Eliminate any existing trace on variables monitored by the menubutton. 498 */ 499 500 if (mbPtr->textVarName != NULL) { 501 Tcl_UntraceVar(interp, mbPtr->textVarName, 502 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 503 MenuButtonTextVarProc, (ClientData) mbPtr); 504 } 505 506 /* 507 * The following loop is potentially executed twice. During the first pass 508 * configuration options get set to their new values. If there is an error 509 * in this pass, we execute a second pass to restore all the options to 510 * their previous values. 511 */ 512 513 for (error = 0; error <= 1; error++) { 514 if (!error) { 515 /* 516 * First pass: set options to new values. 517 */ 518 519 if (Tk_SetOptions(interp, (char *) mbPtr, 520 mbPtr->optionTable, objc, objv, 521 mbPtr->tkwin, &savedOptions, NULL) != TCL_OK) { 522 continue; 523 } 524 } else { 525 /* 526 * Second pass: restore options to old values. 527 */ 528 529 errorResult = Tcl_GetObjResult(interp); 530 Tcl_IncrRefCount(errorResult); 531 Tk_RestoreSavedOptions(&savedOptions); 532 } 533 534 /* 535 * A few options need special processing, such as setting the 536 * background from a 3-D border, or filling in complicated defaults 537 * that couldn't be specified to Tk_SetOptions. 538 */ 539 540 if ((mbPtr->state == STATE_ACTIVE) 541 && !Tk_StrictMotif(mbPtr->tkwin)) { 542 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder); 543 } else { 544 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder); 545 } 546 547 if (mbPtr->highlightWidth < 0) { 548 mbPtr->highlightWidth = 0; 549 } 550 551 if (mbPtr->padX < 0) { 552 mbPtr->padX = 0; 553 } 554 if (mbPtr->padY < 0) { 555 mbPtr->padY = 0; 556 } 557 558 /* 559 * Get the image for the widget, if there is one. Allocate the new 560 * image before freeing the old one, so that the reference count 561 * doesn't go to zero and cause image data to be discarded. 562 */ 563 564 if (mbPtr->imageString != NULL) { 565 image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin, 566 mbPtr->imageString, MenuButtonImageProc, 567 (ClientData) mbPtr); 568 if (image == NULL) { 569 return TCL_ERROR; 570 } 571 } else { 572 image = NULL; 573 } 574 if (mbPtr->image != NULL) { 575 Tk_FreeImage(mbPtr->image); 576 } 577 mbPtr->image = image; 578 579 /* 580 * Recompute the geometry for the button. 581 */ 582 583 if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) { 584 if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString, 585 &mbPtr->width) != TCL_OK) { 586 widthError: 587 Tcl_AddErrorInfo(interp, "\n (processing -width option)"); 588 continue; 589 } 590 if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString, 591 &mbPtr->height) != TCL_OK) { 592 heightError: 593 Tcl_AddErrorInfo(interp, "\n (processing -height option)"); 594 continue; 595 } 596 } else { 597 if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width) 598 != TCL_OK) { 599 goto widthError; 600 } 601 if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height) 602 != TCL_OK) { 603 goto heightError; 604 } 605 } 606 break; 607 } 608 609 if (!error) { 610 Tk_FreeSavedOptions(&savedOptions); 611 } 612 613 if (mbPtr->textVarName != NULL) { 614 /* 615 * If no image or -compound is used, display the value of a variable. 616 * Set up a trace to watch for any changes in it, create the variable 617 * if it doesn't exist, and fetch its current value. 618 */ 619 CONST char *value; 620 621 value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY); 622 if (value == NULL) { 623 Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text, 624 TCL_GLOBAL_ONLY); 625 } else { 626 if (mbPtr->text != NULL) { 627 ckfree(mbPtr->text); 628 } 629 mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1)); 630 strcpy(mbPtr->text, value); 631 } 632 Tcl_TraceVar(interp, mbPtr->textVarName, 633 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 634 MenuButtonTextVarProc, (ClientData) mbPtr); 635 } 636 637 TkMenuButtonWorldChanged((ClientData) mbPtr); 638 if (error) { 639 Tcl_SetObjResult(interp, errorResult); 640 Tcl_DecrRefCount(errorResult); 641 return TCL_ERROR; 642 } 643 return TCL_OK; 644} 645 646/* 647 *--------------------------------------------------------------------------- 648 * 649 * TkMenuButtonWorldChanged -- 650 * 651 * This function is called when the world has changed in some way and the 652 * widget needs to recompute all its graphics contexts and determine its 653 * new geometry. 654 * 655 * Results: 656 * None. 657 * 658 * Side effects: 659 * TkMenuButton will be relayed out and redisplayed. 660 * 661 *--------------------------------------------------------------------------- 662 */ 663 664void 665TkMenuButtonWorldChanged( 666 ClientData instanceData) /* Information about widget. */ 667{ 668 XGCValues gcValues; 669 GC gc; 670 unsigned long mask; 671 TkMenuButton *mbPtr; 672 673 mbPtr = (TkMenuButton *) instanceData; 674 675 gcValues.font = Tk_FontId(mbPtr->tkfont); 676 gcValues.foreground = mbPtr->normalFg->pixel; 677 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; 678 679 /* 680 * Note: GraphicsExpose events are disabled in GC's because they're used 681 * to copy stuff from an off-screen pixmap onto the screen (we know that 682 * there's no problem with obscured areas). 683 */ 684 685 gcValues.graphics_exposures = False; 686 mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures; 687 gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); 688 if (mbPtr->normalTextGC != None) { 689 Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC); 690 } 691 mbPtr->normalTextGC = gc; 692 693 gcValues.foreground = mbPtr->activeFg->pixel; 694 gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel; 695 mask = GCForeground | GCBackground | GCFont; 696 gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); 697 if (mbPtr->activeTextGC != None) { 698 Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC); 699 } 700 mbPtr->activeTextGC = gc; 701 702 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; 703 704 /* 705 * Create the GC that can be used for stippling 706 */ 707 708 if (mbPtr->stippleGC == None) { 709 gcValues.foreground = gcValues.background; 710 mask = GCForeground; 711 if (mbPtr->gray == None) { 712 mbPtr->gray = Tk_GetBitmap(NULL, mbPtr->tkwin, "gray50"); 713 } 714 if (mbPtr->gray != None) { 715 gcValues.fill_style = FillStippled; 716 gcValues.stipple = mbPtr->gray; 717 mask |= GCFillStyle | GCStipple; 718 } 719 mbPtr->stippleGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); 720 } 721 722 /* 723 * Allocate the disabled graphics context, for drawing text in its 724 * disabled state. 725 */ 726 727 mask = GCForeground | GCBackground | GCFont; 728 if (mbPtr->disabledFg != NULL) { 729 gcValues.foreground = mbPtr->disabledFg->pixel; 730 } else { 731 gcValues.foreground = gcValues.background; 732 } 733 gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); 734 if (mbPtr->disabledGC != None) { 735 Tk_FreeGC(mbPtr->display, mbPtr->disabledGC); 736 } 737 mbPtr->disabledGC = gc; 738 739 TkpComputeMenuButtonGeometry(mbPtr); 740 741 /* 742 * Lastly, arrange for the button to be redisplayed. 743 */ 744 745 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { 746 Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); 747 mbPtr->flags |= REDRAW_PENDING; 748 } 749} 750 751/* 752 *-------------------------------------------------------------- 753 * 754 * MenuButtonEventProc -- 755 * 756 * This function is invoked by the Tk dispatcher for various events on 757 * buttons. 758 * 759 * Results: 760 * None. 761 * 762 * Side effects: 763 * When the window gets deleted, internal structures get cleaned up. 764 * When it gets exposed, it is redisplayed. 765 * 766 *-------------------------------------------------------------- 767 */ 768 769static void 770MenuButtonEventProc( 771 ClientData clientData, /* Information about window. */ 772 XEvent *eventPtr) /* Information about event. */ 773{ 774 TkMenuButton *mbPtr = (TkMenuButton *) clientData; 775 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { 776 goto redraw; 777 } else if (eventPtr->type == ConfigureNotify) { 778 /* 779 * Must redraw after size changes, since layout could have changed and 780 * borders will need to be redrawn. 781 */ 782 783 goto redraw; 784 } else if (eventPtr->type == DestroyNotify) { 785 DestroyMenuButton((char *) mbPtr); 786 } else if (eventPtr->type == FocusIn) { 787 if (eventPtr->xfocus.detail != NotifyInferior) { 788 mbPtr->flags |= GOT_FOCUS; 789 if (mbPtr->highlightWidth > 0) { 790 goto redraw; 791 } 792 } 793 } else if (eventPtr->type == FocusOut) { 794 if (eventPtr->xfocus.detail != NotifyInferior) { 795 mbPtr->flags &= ~GOT_FOCUS; 796 if (mbPtr->highlightWidth > 0) { 797 goto redraw; 798 } 799 } 800 } 801 return; 802 803 redraw: 804 if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) { 805 Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); 806 mbPtr->flags |= REDRAW_PENDING; 807 } 808} 809 810/* 811 *---------------------------------------------------------------------- 812 * 813 * MenuButtonCmdDeletedProc -- 814 * 815 * This function is invoked when a widget command is deleted. If the 816 * widget isn't already in the process of being destroyed, this command 817 * destroys it. 818 * 819 * Results: 820 * None. 821 * 822 * Side effects: 823 * The widget is destroyed. 824 * 825 *---------------------------------------------------------------------- 826 */ 827 828static void 829MenuButtonCmdDeletedProc( 830 ClientData clientData) /* Pointer to widget record for widget. */ 831{ 832 TkMenuButton *mbPtr = (TkMenuButton *) clientData; 833 Tk_Window tkwin = mbPtr->tkwin; 834 835 /* 836 * This function could be invoked either because the window was destroyed 837 * and the command was then deleted (in which case tkwin is NULL) or 838 * because the command was deleted, and then this function destroys the 839 * widget. 840 */ 841 842 if (tkwin != NULL) { 843 Tk_DestroyWindow(tkwin); 844 } 845} 846 847/* 848 *-------------------------------------------------------------- 849 * 850 * MenuButtonTextVarProc -- 851 * 852 * This function is invoked when someone changes the variable whose 853 * contents are to be displayed in a menu button. 854 * 855 * Results: 856 * NULL is always returned. 857 * 858 * Side effects: 859 * The text displayed in the menu button will change to match the 860 * variable. 861 * 862 *-------------------------------------------------------------- 863 */ 864 865 /* ARGSUSED */ 866static char * 867MenuButtonTextVarProc( 868 ClientData clientData, /* Information about button. */ 869 Tcl_Interp *interp, /* Interpreter containing variable. */ 870 CONST char *name1, /* Name of variable. */ 871 CONST char *name2, /* Second part of variable name. */ 872 int flags) /* Information about what happened. */ 873{ 874 register TkMenuButton *mbPtr = (TkMenuButton *) clientData; 875 CONST char *value; 876 unsigned len; 877 878 /* 879 * If the variable is unset, then immediately recreate it unless the whole 880 * interpreter is going away. 881 */ 882 883 if (flags & TCL_TRACE_UNSETS) { 884 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 885 Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text, 886 TCL_GLOBAL_ONLY); 887 Tcl_TraceVar(interp, mbPtr->textVarName, 888 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 889 MenuButtonTextVarProc, clientData); 890 } 891 return NULL; 892 } 893 894 value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY); 895 if (value == NULL) { 896 value = ""; 897 } 898 if (mbPtr->text != NULL) { 899 ckfree(mbPtr->text); 900 } 901 len = 1 + (unsigned) strlen(value); 902 mbPtr->text = (char *) ckalloc(len); 903 memcpy(mbPtr->text, value, len); 904 TkpComputeMenuButtonGeometry(mbPtr); 905 906 if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin) 907 && !(mbPtr->flags & REDRAW_PENDING)) { 908 Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); 909 mbPtr->flags |= REDRAW_PENDING; 910 } 911 return NULL; 912} 913 914/* 915 *---------------------------------------------------------------------- 916 * 917 * MenuButtonImageProc -- 918 * 919 * This function is invoked by the image code whenever the manager for an 920 * image does something that affects the size of contents of an image 921 * displayed in a button. 922 * 923 * Results: 924 * None. 925 * 926 * Side effects: 927 * Arranges for the button to get redisplayed. 928 * 929 *---------------------------------------------------------------------- 930 */ 931 932static void 933MenuButtonImageProc( 934 ClientData clientData, /* Pointer to widget record. */ 935 int x, int y, /* Upper left pixel (within image) that must 936 * be redisplayed. */ 937 int width, int height, /* Dimensions of area to redisplay (may be <= 938 * 0). */ 939 int imgWidth, int imgHeight)/* New dimensions of image. */ 940{ 941 register TkMenuButton *mbPtr = (TkMenuButton *) clientData; 942 943 if (mbPtr->tkwin != NULL) { 944 TkpComputeMenuButtonGeometry(mbPtr); 945 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { 946 Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); 947 mbPtr->flags |= REDRAW_PENDING; 948 } 949 } 950} 951 952/* 953 * Local Variables: 954 * mode: c 955 * c-basic-offset: 4 956 * fill-column: 78 957 * End: 958 */ 959