1/* 2 * tkUnixMenu.c -- 3 * 4 * This module implements the UNIX platform-specific features of menus. 5 * 6 * Copyright (c) 1996-1998 by Sun Microsystems, Inc. 7 * 8 * See the file "license.terms" for information on usage and redistribution of 9 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 10 * 11 * RCS: @(#) $Id$ 12 */ 13 14#include "default.h" 15#include "tkUnixInt.h" 16#include "tkMenu.h" 17 18/* 19 * Constants used for menu drawing. 20 */ 21 22#define MENU_MARGIN_WIDTH 2 23#define MENU_DIVIDER_HEIGHT 2 24 25/* 26 * Platform specific flags for Unix. 27 */ 28 29#define ENTRY_HELP_MENU ENTRY_PLATFORM_FLAG1 30 31/* 32 * Shared with button widget. 33 */ 34 35MODULE_SCOPE void TkpDrawCheckIndicator(Tk_Window tkwin, 36 Display *display, Drawable d, int x, int y, 37 Tk_3DBorder bgBorder, XColor *indicatorColor, 38 XColor *selectColor, XColor *disColor, int on, 39 int disabled, int mode); 40/* 41 * Indicator Draw Modes 42 */ 43 44#define CHECK_BUTTON 0 45#define CHECK_MENU 1 46#define RADIO_BUTTON 2 47#define RADIO_MENU 3 48 49/* 50 * Procedures used internally. 51 */ 52 53static void SetHelpMenu(TkMenu *menuPtr); 54static void DrawMenuEntryAccelerator(TkMenu *menuPtr, 55 TkMenuEntry *mePtr, Drawable d, GC gc, 56 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, 57 Tk_3DBorder activeBorder, int x, int y, 58 int width, int height, int drawArrow); 59static void DrawMenuEntryBackground(TkMenu *menuPtr, 60 TkMenuEntry *mePtr, Drawable d, 61 Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, 62 int x, int y, int width, int heigth); 63static void DrawMenuEntryIndicator(TkMenu *menuPtr, 64 TkMenuEntry *mePtr, Drawable d, 65 Tk_3DBorder border, XColor *indicatorColor, 66 XColor *disableColor, Tk_Font tkfont, 67 CONST Tk_FontMetrics *fmPtr, int x, int y, 68 int width, int height); 69static void DrawMenuEntryLabel(TkMenu * menuPtr, 70 TkMenuEntry *mePtr, Drawable d, GC gc, 71 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, 72 int x, int y, int width, int height); 73static void DrawMenuSeparator(TkMenu *menuPtr, 74 TkMenuEntry *mePtr, Drawable d, GC gc, 75 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, 76 int x, int y, int width, int height); 77static void DrawTearoffEntry(TkMenu *menuPtr, 78 TkMenuEntry *mePtr, Drawable d, GC gc, 79 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, 80 int x, int y, int width, int height); 81static void DrawMenuUnderline(TkMenu *menuPtr, 82 TkMenuEntry *mePtr, Drawable d, GC gc, 83 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, 84 int x, int y, int width, int height); 85static void GetMenuAccelGeometry(TkMenu *menuPtr, 86 TkMenuEntry *mePtr, Tk_Font tkfont, 87 CONST Tk_FontMetrics *fmPtr, int *widthPtr, 88 int *heightPtr); 89static void GetMenuLabelGeometry(TkMenuEntry *mePtr, 90 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, 91 int *widthPtr, int *heightPtr); 92static void GetMenuIndicatorGeometry(TkMenu *menuPtr, 93 TkMenuEntry *mePtr, Tk_Font tkfont, 94 CONST Tk_FontMetrics *fmPtr, 95 int *widthPtr, int *heightPtr); 96static void GetMenuSeparatorGeometry(TkMenu *menuPtr, 97 TkMenuEntry *mePtr, Tk_Font tkfont, 98 CONST Tk_FontMetrics *fmPtr, 99 int *widthPtr, int *heightPtr); 100static void GetTearoffEntryGeometry(TkMenu *menuPtr, 101 TkMenuEntry *mePtr, Tk_Font tkfont, 102 CONST Tk_FontMetrics *fmPtr, int *widthPtr, 103 int *heightPtr); 104 105/* 106 *---------------------------------------------------------------------- 107 * 108 * TkpNewMenu -- 109 * 110 * Gets the platform-specific piece of the menu. Invoked during idle 111 * after the generic part of the menu has been created. 112 * 113 * Results: 114 * Standard TCL error. 115 * 116 * Side effects: 117 * Allocates any platform specific allocations and places them in the 118 * platformData field of the menuPtr. 119 * 120 *---------------------------------------------------------------------- 121 */ 122 123int 124TkpNewMenu( 125 TkMenu *menuPtr) 126{ 127 SetHelpMenu(menuPtr); 128 return TCL_OK; 129} 130 131/* 132 *---------------------------------------------------------------------- 133 * 134 * TkpDestroyMenu -- 135 * 136 * Destroys platform-specific menu structures. Called when the generic 137 * menu structure is destroyed for the menu. 138 * 139 * Results: 140 * None. 141 * 142 * Side effects: 143 * All platform-specific allocations are freed up. 144 * 145 *---------------------------------------------------------------------- 146 */ 147 148void 149TkpDestroyMenu( 150 TkMenu *menuPtr) 151{ 152 /* 153 * Nothing to do. 154 */ 155} 156 157/* 158 *---------------------------------------------------------------------- 159 * 160 * TkpDestroyMenuEntry -- 161 * 162 * Cleans up platform-specific menu entry items. Called when entry is 163 * destroyed in the generic code. 164 * 165 * Results: 166 * None. 167 * 168 * Side effects: 169 * All platform specific allocations are freed up. 170 * 171 *---------------------------------------------------------------------- 172 */ 173 174void 175TkpDestroyMenuEntry( 176 TkMenuEntry *mEntryPtr) 177{ 178 /* 179 * Nothing to do. 180 */ 181} 182 183/* 184 *---------------------------------------------------------------------- 185 * 186 * TkpConfigureMenuEntry -- 187 * 188 * Processes configuration options for menu entries. Called when the 189 * generic options are processed for the menu. 190 * 191 * Results: 192 * Returns standard TCL result. If TCL_ERROR is returned, then the 193 * interp's result contains an error message. 194 * 195 * Side effects: 196 * Configuration information get set for mePtr; old resources get freed, 197 * if any need it. 198 * 199 *---------------------------------------------------------------------- 200 */ 201 202int 203TkpConfigureMenuEntry( 204 register TkMenuEntry *mePtr)/* Information about menu entry; may or may 205 * not already have values for some fields. */ 206{ 207 /* 208 * If this is a cascade menu, and the child menu exists, check to see if 209 * the child menu is a help menu. 210 */ 211 212 if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) { 213 TkMenuReferences *menuRefPtr; 214 215 menuRefPtr = TkFindMenuReferencesObj(mePtr->menuPtr->interp, 216 mePtr->namePtr); 217 if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) { 218 SetHelpMenu(menuRefPtr->menuPtr); 219 } 220 } 221 return TCL_OK; 222} 223 224/* 225 *---------------------------------------------------------------------- 226 * 227 * TkpMenuNewEntry -- 228 * 229 * Called when a new entry is created in a menu. Fills in platform 230 * specific data for the entry. The platformEntryData field is used to 231 * store the indicator diameter for radio button and check box entries. 232 * 233 * Results: 234 * Standard TCL error. 235 * 236 * Side effects: 237 * None on Unix. 238 * 239 *---------------------------------------------------------------------- 240 */ 241 242int 243TkpMenuNewEntry( 244 TkMenuEntry *mePtr) 245{ 246 return TCL_OK; 247} 248 249/* 250 *---------------------------------------------------------------------- 251 * 252 * TkpSetWindowMenuBar -- 253 * 254 * Sets up the menu as a menubar in the given window. 255 * 256 * Results: 257 * None. 258 * 259 * Side effects: 260 * Recomputes geometry of given window. 261 * 262 *---------------------------------------------------------------------- 263 */ 264 265void 266TkpSetWindowMenuBar( 267 Tk_Window tkwin, /* The window we are setting */ 268 TkMenu *menuPtr) /* The menu we are setting */ 269{ 270 if (menuPtr == NULL) { 271 TkUnixSetMenubar(tkwin, NULL); 272 } else { 273 TkUnixSetMenubar(tkwin, menuPtr->tkwin); 274 } 275} 276 277/* 278 *---------------------------------------------------------------------- 279 * 280 * TkpSetMainMenuBar -- 281 * 282 * Called when a toplevel widget is brought to front. On the Macintosh, 283 * sets up the menubar that goes accross the top of the main monitor. On 284 * other platforms, nothing is necessary. 285 * 286 * Results: 287 * None. 288 * 289 * Side effects: 290 * Recompute geometry of given window. 291 * 292 *---------------------------------------------------------------------- 293 */ 294 295void 296TkpSetMainMenubar( 297 Tcl_Interp *interp, 298 Tk_Window tkwin, 299 char *menuName) 300{ 301 /* 302 * Nothing to do. 303 */ 304} 305 306/* 307 *---------------------------------------------------------------------- 308 * 309 * GetMenuIndicatorGeometry -- 310 * 311 * Fills out the geometry of the indicator in a menu item. Note that the 312 * mePtr->height field must have already been filled in by 313 * GetMenuLabelGeometry since this height depends on the label height. 314 * 315 * Results: 316 * widthPtr and heightPtr point to the new geometry values. 317 * 318 * Side effects: 319 * None. 320 * 321 *---------------------------------------------------------------------- 322 */ 323 324static void 325GetMenuIndicatorGeometry( 326 TkMenu *menuPtr, /* The menu we are drawing. */ 327 TkMenuEntry *mePtr, /* The entry we are interested in. */ 328 Tk_Font tkfont, /* The precalculated font */ 329 CONST Tk_FontMetrics *fmPtr,/* The precalculated metrics */ 330 int *widthPtr, /* The resulting width */ 331 int *heightPtr) /* The resulting height */ 332{ 333 if ((mePtr->type == CHECK_BUTTON_ENTRY) 334 || (mePtr->type == RADIO_BUTTON_ENTRY)) { 335 if (!mePtr->hideMargin && mePtr->indicatorOn) { 336 if ((mePtr->image != NULL) || (mePtr->bitmapPtr != NULL)) { 337 *widthPtr = (14 * mePtr->height) / 10; 338 *heightPtr = mePtr->height; 339 if (mePtr->type == CHECK_BUTTON_ENTRY) { 340 mePtr->platformEntryData = (TkMenuPlatformEntryData) 341 INT2PTR((65 * mePtr->height) / 100); 342 } else { 343 mePtr->platformEntryData = (TkMenuPlatformEntryData) 344 INT2PTR((75 * mePtr->height) / 100); 345 } 346 } else { 347 *widthPtr = *heightPtr = mePtr->height; 348 if (mePtr->type == CHECK_BUTTON_ENTRY) { 349 mePtr->platformEntryData = (TkMenuPlatformEntryData) 350 INT2PTR((80 * mePtr->height) / 100); 351 } else { 352 mePtr->platformEntryData = (TkMenuPlatformEntryData) 353 INT2PTR(mePtr->height); 354 } 355 } 356 } else { 357 int borderWidth; 358 359 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, 360 menuPtr->borderWidthPtr, &borderWidth); 361 *heightPtr = 0; 362 *widthPtr = borderWidth; 363 } 364 } else { 365 int borderWidth; 366 367 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 368 &borderWidth); 369 *heightPtr = 0; 370 *widthPtr = borderWidth; 371 } 372} 373 374 375/* 376 *---------------------------------------------------------------------- 377 * 378 * GetMenuAccelGeometry -- 379 * 380 * Get the geometry of the accelerator area of a menu item. 381 * 382 * Results: 383 * heightPtr and widthPtr are set. 384 * 385 * Side effects: 386 * None. 387 * 388 *---------------------------------------------------------------------- 389 */ 390 391static void 392GetMenuAccelGeometry( 393 TkMenu *menuPtr, /* The menu was are drawing */ 394 TkMenuEntry *mePtr, /* The entry we are getting the geometry for */ 395 Tk_Font tkfont, /* The precalculated font */ 396 CONST Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 397 int *widthPtr, /* The width of the acclerator area */ 398 int *heightPtr) /* The height of the accelerator area */ 399{ 400 *heightPtr = fmPtr->linespace; 401 if (mePtr->type == CASCADE_ENTRY) { 402 *widthPtr = 2 * CASCADE_ARROW_WIDTH; 403 } else if ((menuPtr->menuType != MENUBAR) 404 && (mePtr->accelPtr != NULL)) { 405 char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, NULL); 406 407 *widthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength); 408 } else { 409 *widthPtr = 0; 410 } 411} 412 413/* 414 *---------------------------------------------------------------------- 415 * 416 * DrawMenuEntryBackground -- 417 * 418 * This procedure draws the background part of a menu. 419 * 420 * Results: 421 * None. 422 * 423 * Side effects: 424 * Commands are output to X to display the menu in its current mode. 425 * 426 *---------------------------------------------------------------------- 427 */ 428 429static void 430DrawMenuEntryBackground( 431 TkMenu *menuPtr, /* The menu we are drawing */ 432 TkMenuEntry *mePtr, /* The entry we are drawing. */ 433 Drawable d, /* The drawable we are drawing into */ 434 Tk_3DBorder activeBorder, /* The border for an active item */ 435 Tk_3DBorder bgBorder, /* The background border */ 436 int x, /* Left coordinate of entry rect */ 437 int y, /* Right coordinate of entry rect */ 438 int width, /* Width of entry rect */ 439 int height) /* Height of entry rect */ 440{ 441 if (mePtr->state == ENTRY_ACTIVE) { 442 int relief; 443 int activeBorderWidth; 444 445 bgBorder = activeBorder; 446 447 if ((menuPtr->menuType == MENUBAR) 448 && ((menuPtr->postedCascade == NULL) 449 || (menuPtr->postedCascade != mePtr))) { 450 relief = TK_RELIEF_FLAT; 451 } else { 452 relief = TK_RELIEF_RAISED; 453 } 454 455 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, 456 menuPtr->activeBorderWidthPtr, &activeBorderWidth); 457 Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height, 458 activeBorderWidth, relief); 459 } else { 460 Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height, 461 0, TK_RELIEF_FLAT); 462 } 463} 464 465/* 466 *---------------------------------------------------------------------- 467 * 468 * DrawMenuEntryAccelerator -- 469 * 470 * This procedure draws the background part of a menu. 471 * 472 * Results: 473 * None. 474 * 475 * Side effects: 476 * Commands are output to X to display the menu in its current mode. 477 * 478 *---------------------------------------------------------------------- 479 */ 480 481static void 482DrawMenuEntryAccelerator( 483 TkMenu *menuPtr, /* The menu we are drawing */ 484 TkMenuEntry *mePtr, /* The entry we are drawing */ 485 Drawable d, /* The drawable we are drawing into */ 486 GC gc, /* The precalculated gc to draw with */ 487 Tk_Font tkfont, /* The precalculated font */ 488 CONST Tk_FontMetrics *fmPtr,/* The precalculated metrics */ 489 Tk_3DBorder activeBorder, /* The border for an active item */ 490 int x, /* Left coordinate of entry rect */ 491 int y, /* Top coordinate of entry rect */ 492 int width, /* Width of entry */ 493 int height, /* Height of entry */ 494 int drawArrow) /* Whether or not to draw arrow. */ 495{ 496 XPoint points[3]; 497 int borderWidth, activeBorderWidth; 498 499 /* 500 * Draw accelerator or cascade arrow. 501 */ 502 503 if (menuPtr->menuType == MENUBAR) { 504 return; 505 } 506 507 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 508 &borderWidth); 509 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, 510 &activeBorderWidth); 511 if ((mePtr->type == CASCADE_ENTRY) && drawArrow) { 512 points[0].x = x + width - borderWidth - activeBorderWidth 513 - CASCADE_ARROW_WIDTH; 514 points[0].y = y + (height - CASCADE_ARROW_HEIGHT)/2; 515 points[1].x = points[0].x; 516 points[1].y = points[0].y + CASCADE_ARROW_HEIGHT; 517 points[2].x = points[0].x + CASCADE_ARROW_WIDTH; 518 points[2].y = points[0].y + CASCADE_ARROW_HEIGHT/2; 519 Tk_Fill3DPolygon(menuPtr->tkwin, d, activeBorder, points, 3, 520 DECORATION_BORDER_WIDTH, 521 (menuPtr->postedCascade == mePtr) 522 ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); 523 } else if (mePtr->accelPtr != NULL) { 524 char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, NULL); 525 int left = x + mePtr->labelWidth + activeBorderWidth 526 + mePtr->indicatorSpace; 527 528 if (menuPtr->menuType == MENUBAR) { 529 left += 5; 530 } 531 Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel, 532 mePtr->accelLength, left, 533 (y + (height + fmPtr->ascent - fmPtr->descent) / 2)); 534 } 535} 536 537/* 538 *---------------------------------------------------------------------- 539 * 540 * DrawMenuEntryIndicator -- 541 * 542 * This procedure draws the background part of a menu. 543 * 544 * Results: 545 * None. 546 * 547 * Side effects: 548 * Commands are output to X to display the menu in its current mode. 549 * 550 *---------------------------------------------------------------------- 551 */ 552 553static void 554DrawMenuEntryIndicator( 555 TkMenu *menuPtr, /* The menu we are drawing */ 556 TkMenuEntry *mePtr, /* The entry we are drawing */ 557 Drawable d, /* The drawable to draw into */ 558 Tk_3DBorder border, /* The background color */ 559 XColor *indicatorColor, /* The color to draw indicators with */ 560 XColor *disableColor, /* The color use use when disabled */ 561 Tk_Font tkfont, /* The font to draw with */ 562 CONST Tk_FontMetrics *fmPtr,/* The font metrics of the font */ 563 int x, /* The left of the entry rect */ 564 int y, /* The top of the entry rect */ 565 int width, /* Width of menu entry */ 566 int height) /* Height of menu entry */ 567{ 568 /* 569 * Draw check-button indicator. 570 */ 571 572 if ((mePtr->type == CHECK_BUTTON_ENTRY) && mePtr->indicatorOn) { 573 int top, left, activeBorderWidth; 574 int disabled = (mePtr->state == ENTRY_DISABLED); 575 XColor *bg; 576 577 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, 578 menuPtr->activeBorderWidthPtr, &activeBorderWidth); 579 bg = Tk_3DBorderColor(border); 580 top = y + height/2; 581 left = x + activeBorderWidth + DECORATION_BORDER_WIDTH 582 + mePtr->indicatorSpace/2; 583 584 TkpDrawCheckIndicator(menuPtr->tkwin, menuPtr->display, d, left, top, 585 border, indicatorColor, bg, disableColor, 586 (mePtr->entryFlags & ENTRY_SELECTED), disabled, CHECK_MENU); 587 } 588 589 /* 590 * Draw radio-button indicator. 591 */ 592 593 if ((mePtr->type == RADIO_BUTTON_ENTRY) && mePtr->indicatorOn) { 594 int top, left, activeBorderWidth; 595 int disabled = (mePtr->state == ENTRY_DISABLED); 596 XColor *bg; 597 598 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, 599 menuPtr->activeBorderWidthPtr, &activeBorderWidth); 600 bg = Tk_3DBorderColor(border); 601 top = y + height/2; 602 left = x + activeBorderWidth + DECORATION_BORDER_WIDTH 603 + mePtr->indicatorSpace/2; 604 605 TkpDrawCheckIndicator(menuPtr->tkwin, menuPtr->display, d, left, top, 606 border, indicatorColor, bg, disableColor, 607 (mePtr->entryFlags & ENTRY_SELECTED), disabled, RADIO_MENU); 608 } 609} 610 611/* 612 *---------------------------------------------------------------------- 613 * 614 * DrawMenuSeparator -- 615 * 616 * This procedure draws a separator menu item. 617 * 618 * Results: 619 * None. 620 * 621 * Side effects: 622 * Commands are output to X to display the menu in its current mode. 623 * 624 *---------------------------------------------------------------------- 625 */ 626 627static void 628DrawMenuSeparator( 629 TkMenu *menuPtr, /* The menu we are drawing */ 630 TkMenuEntry *mePtr, /* The entry we are drawing */ 631 Drawable d, /* The drawable we are using */ 632 GC gc, /* The gc to draw into */ 633 Tk_Font tkfont, /* The font to draw with */ 634 CONST Tk_FontMetrics *fmPtr,/* The font metrics from the font */ 635 int x, int y, 636 int width, int height) 637{ 638 XPoint points[2]; 639 Tk_3DBorder border; 640 641 if (menuPtr->menuType == MENUBAR) { 642 return; 643 } 644 645 points[0].x = x; 646 points[0].y = y + height/2; 647 points[1].x = width - 1; 648 points[1].y = points[0].y; 649 border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); 650 Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, 651 TK_RELIEF_RAISED); 652} 653 654/* 655 *---------------------------------------------------------------------- 656 * 657 * DrawMenuEntryLabel -- 658 * 659 * This procedure draws the label part of a menu. 660 * 661 * Results: 662 * None. 663 * 664 * Side effects: 665 * Commands are output to X to display the menu in its current mode. 666 * 667 *---------------------------------------------------------------------- 668 */ 669 670static void 671DrawMenuEntryLabel( 672 TkMenu *menuPtr, /* The menu we are drawing. */ 673 TkMenuEntry *mePtr, /* The entry we are drawing. */ 674 Drawable d, /* What we are drawing into. */ 675 GC gc, /* The gc we are drawing into.*/ 676 Tk_Font tkfont, /* The precalculated font. */ 677 CONST Tk_FontMetrics *fmPtr,/* The precalculated font metrics. */ 678 int x, /* Left edge. */ 679 int y, /* Top edge. */ 680 int width, /* width of entry. */ 681 int height) /* height of entry. */ 682{ 683 int indicatorSpace = mePtr->indicatorSpace; 684 int activeBorderWidth, leftEdge, imageHeight, imageWidth; 685 int textHeight = 0, textWidth = 0; /* stop GCC warning */ 686 int haveImage = 0, haveText = 0; 687 int imageXOffset = 0, imageYOffset = 0; 688 int textXOffset = 0, textYOffset = 0; 689 690 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, 691 &activeBorderWidth); 692 leftEdge = x + indicatorSpace + activeBorderWidth; 693 if (menuPtr->menuType == MENUBAR) { 694 leftEdge += 5; 695 } 696 697 /* 698 * Work out what we will need to draw first. 699 */ 700 701 if (mePtr->image != NULL) { 702 Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); 703 haveImage = 1; 704 } else if (mePtr->bitmapPtr != NULL) { 705 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); 706 Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight); 707 haveImage = 1; 708 } 709 if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { 710 if (mePtr->labelLength > 0) { 711 char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL); 712 textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength); 713 textHeight = fmPtr->linespace; 714 haveText = 1; 715 } 716 } 717 718 /* 719 * Now work out what the relative positions are. 720 */ 721 722 if (haveImage && haveText) { 723 int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth); 724 switch ((enum compound) mePtr->compound) { 725 case COMPOUND_TOP: 726 textXOffset = (fullWidth - textWidth)/2; 727 textYOffset = imageHeight/2 + 2; 728 imageXOffset = (fullWidth - imageWidth)/2; 729 imageYOffset = -textHeight/2; 730 break; 731 case COMPOUND_BOTTOM: 732 textXOffset = (fullWidth - textWidth)/2; 733 textYOffset = -imageHeight/2; 734 imageXOffset = (fullWidth - imageWidth)/2; 735 imageYOffset = textHeight/2 + 2; 736 break; 737 case COMPOUND_LEFT: 738 /* 739 * Position image in the indicator space to the left of the 740 * entries, unless this entry is a radio|check button because then 741 * the indicator space will be used. 742 */ 743 744 textXOffset = imageWidth + 2; 745 textYOffset = 0; 746 imageXOffset = 0; 747 imageYOffset = 0; 748 if ((mePtr->type != CHECK_BUTTON_ENTRY) 749 && (mePtr->type != RADIO_BUTTON_ENTRY)) { 750 textXOffset -= indicatorSpace; 751 if (textXOffset < 0) { 752 textXOffset = 0; 753 } 754 imageXOffset = -indicatorSpace; 755 } 756 break; 757 case COMPOUND_RIGHT: 758 textXOffset = 0; 759 textYOffset = 0; 760 imageXOffset = textWidth + 2; 761 imageYOffset = 0; 762 break; 763 case COMPOUND_CENTER: 764 textXOffset = (fullWidth - textWidth)/2; 765 textYOffset = 0; 766 imageXOffset = (fullWidth - imageWidth)/2; 767 imageYOffset = 0; 768 break; 769 case COMPOUND_NONE: 770 break; 771 } 772 } else { 773 textXOffset = 0; 774 textYOffset = 0; 775 imageXOffset = 0; 776 imageYOffset = 0; 777 } 778 779 /* 780 * Draw label and/or bitmap or image for entry. 781 */ 782 783 if (mePtr->image != NULL) { 784 if ((mePtr->selectImage != NULL) 785 && (mePtr->entryFlags & ENTRY_SELECTED)) { 786 Tk_RedrawImage(mePtr->selectImage, 0, 0, 787 imageWidth, imageHeight, d, leftEdge + imageXOffset, 788 (int) (y + (mePtr->height-imageHeight)/2 + imageYOffset)); 789 } else { 790 Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, 791 imageHeight, d, leftEdge + imageXOffset, 792 (int) (y + (mePtr->height-imageHeight)/2 + imageYOffset)); 793 } 794 } else if (mePtr->bitmapPtr != None) { 795 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); 796 797 XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, 798 (unsigned) imageWidth, (unsigned) imageHeight, 799 leftEdge + imageXOffset, 800 (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 1); 801 } 802 if ((mePtr->compound != COMPOUND_NONE) || !haveImage) { 803 int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; 804 805 if (mePtr->labelLength > 0) { 806 char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL); 807 808 Tk_DrawChars(menuPtr->display, d, gc, tkfont, label, 809 mePtr->labelLength, leftEdge + textXOffset, 810 baseline + textYOffset); 811 DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, 812 x + textXOffset, y + textYOffset, 813 width, height); 814 } 815 } 816 817 if (mePtr->state == ENTRY_DISABLED) { 818 if (menuPtr->disabledFgPtr == NULL) { 819 XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y, 820 (unsigned) width, (unsigned) height); 821 } else if ((mePtr->image != NULL) 822 && (menuPtr->disabledImageGC != None)) { 823 XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC, 824 leftEdge + imageXOffset, 825 (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 826 (unsigned) imageWidth, (unsigned) imageHeight); 827 } 828 } 829} 830 831/* 832 *---------------------------------------------------------------------- 833 * 834 * DrawMenuUnderline -- 835 * 836 * On appropriate platforms, draw the underline character for the menu. 837 * 838 * Results: 839 * None. 840 * 841 * Side effects: 842 * Commands are output to X to display the menu in its current mode. 843 * 844 *---------------------------------------------------------------------- 845 */ 846 847static void 848DrawMenuUnderline( 849 TkMenu *menuPtr, /* The menu to draw into */ 850 TkMenuEntry *mePtr, /* The entry we are drawing */ 851 Drawable d, /* What we are drawing into */ 852 GC gc, /* The gc to draw into */ 853 Tk_Font tkfont, /* The precalculated font */ 854 CONST Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 855 int x, int y, 856 int width, int height) 857{ 858 if ((mePtr->underline >= 0) && (mePtr->labelPtr != NULL)) { 859 int len; 860 861 /* 862 * Do the unicode call just to prevent overruns. 863 */ 864 865 Tcl_GetUnicodeFromObj(mePtr->labelPtr, &len); 866 if (mePtr->underline < len) { 867 int activeBorderWidth, leftEdge; 868 CONST char *label, *start, *end; 869 870 label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL); 871 start = Tcl_UtfAtIndex(label, mePtr->underline); 872 end = Tcl_UtfNext(start); 873 874 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, 875 menuPtr->activeBorderWidthPtr, &activeBorderWidth); 876 leftEdge = x + mePtr->indicatorSpace + activeBorderWidth; 877 if (menuPtr->menuType == MENUBAR) { 878 leftEdge += 5; 879 } 880 881 Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, label, leftEdge, 882 y + (height + fmPtr->ascent - fmPtr->descent) / 2, 883 start - label, end - label); 884 } 885 } 886} 887 888/* 889 *---------------------------------------------------------------------- 890 * 891 * TkpPostMenu -- 892 * 893 * Posts a menu on the screen 894 * 895 * Results: 896 * None. 897 * 898 * Side effects: 899 * The menu is posted and handled. 900 * 901 *---------------------------------------------------------------------- 902 */ 903 904int 905TkpPostMenu( 906 Tcl_Interp *interp, 907 TkMenu *menuPtr, 908 int x, int y) 909{ 910 return TkPostTearoffMenu(interp, menuPtr, x, y); 911} 912 913/* 914 *---------------------------------------------------------------------- 915 * 916 * GetMenuSeparatorGeometry -- 917 * 918 * Gets the width and height of the indicator area of a menu. 919 * 920 * Results: 921 * widthPtr and heightPtr are set. 922 * 923 * Side effects: 924 * None. 925 * 926 *---------------------------------------------------------------------- 927 */ 928 929static void 930GetMenuSeparatorGeometry( 931 TkMenu *menuPtr, /* The menu we are measuring */ 932 TkMenuEntry *mePtr, /* The entry we are measuring */ 933 Tk_Font tkfont, /* The precalculated font */ 934 CONST Tk_FontMetrics *fmPtr,/* The precalcualted font metrics */ 935 int *widthPtr, /* The resulting width */ 936 int *heightPtr) /* The resulting height */ 937{ 938 *widthPtr = 0; 939 *heightPtr = fmPtr->linespace; 940} 941 942/* 943 *---------------------------------------------------------------------- 944 * 945 * GetTearoffEntryGeometry -- 946 * 947 * Gets the width and height of the indicator area of a menu. 948 * 949 * Results: 950 * widthPtr and heightPtr are set. 951 * 952 * Side effects: 953 * None. 954 * 955 *---------------------------------------------------------------------- 956 */ 957 958static void 959GetTearoffEntryGeometry( 960 TkMenu *menuPtr, /* The menu we are drawing */ 961 TkMenuEntry *mePtr, /* The entry we are measuring */ 962 Tk_Font tkfont, /* The precalculated font */ 963 CONST Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 964 int *widthPtr, /* The resulting width */ 965 int *heightPtr) /* The resulting height */ 966{ 967 if (menuPtr->menuType != MASTER_MENU) { 968 *heightPtr = 0; 969 *widthPtr = 0; 970 } else { 971 *heightPtr = fmPtr->linespace; 972 *widthPtr = Tk_TextWidth(tkfont, "W", 1); 973 } 974} 975 976/* 977 *-------------------------------------------------------------- 978 * 979 * TkpComputeMenubarGeometry -- 980 * 981 * This procedure is invoked to recompute the size and layout of a menu 982 * that is a menubar clone. 983 * 984 * Results: 985 * None. 986 * 987 * Side effects: 988 * Fields of menu entries are changed to reflect their current positions, 989 * and the size of the menu window itself may be changed. 990 * 991 *-------------------------------------------------------------- 992 */ 993 994void 995TkpComputeMenubarGeometry( 996 TkMenu *menuPtr) /* Structure describing menu. */ 997{ 998 Tk_Font tkfont, menuFont; 999 Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; 1000 int width, height, i, j, x, y, currentRowHeight, maxWidth; 1001 int maxWindowWidth, lastRowBreak, lastEntry; 1002 int borderWidth, activeBorderWidth, helpMenuIndex = -1; 1003 TkMenuEntry *mePtr; 1004 1005 if (menuPtr->tkwin == NULL) { 1006 return; 1007 } 1008 1009 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 1010 &borderWidth); 1011 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, 1012 &activeBorderWidth); 1013 maxWidth = 0; 1014 if (menuPtr->numEntries == 0) { 1015 height = 0; 1016 } else { 1017 int borderWidth; 1018 1019 maxWindowWidth = Tk_Width(menuPtr->tkwin); 1020 if (maxWindowWidth == 1) { 1021 maxWindowWidth = 0x7ffffff; 1022 } 1023 currentRowHeight = 0; 1024 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 1025 &borderWidth); 1026 x = y = borderWidth; 1027 lastRowBreak = 0; 1028 1029 /* 1030 * On the Mac especially, getting font metrics can be quite slow, so 1031 * we want to do it intelligently. We are going to precalculate them 1032 * and pass them down to all of the measureing and drawing routines. 1033 * We will measure the font metrics of the menu once, and if an entry 1034 * has a font set, we will measure it as we come to it, and then we 1035 * decide which set to give the geometry routines. 1036 */ 1037 1038 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 1039 Tk_GetFontMetrics(menuFont, &menuMetrics); 1040 1041 for (i = 0; i < menuPtr->numEntries; i++) { 1042 mePtr = menuPtr->entries[i]; 1043 mePtr->entryFlags &= ~ENTRY_LAST_COLUMN; 1044 if (mePtr->fontPtr != NULL) { 1045 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); 1046 Tk_GetFontMetrics(tkfont, &entryMetrics); 1047 fmPtr = &entryMetrics; 1048 } else { 1049 tkfont = menuFont; 1050 fmPtr = &menuMetrics; 1051 } 1052 1053 /* 1054 * For every entry, we need to check to see whether or not we 1055 * wrap. If we do wrap, then we have to adjust all of the previous 1056 * entries' height and y position, because when we see them the 1057 * first time, we don't know how big its neighbor might be. 1058 */ 1059 1060 if ((mePtr->type == SEPARATOR_ENTRY) 1061 || (mePtr->type == TEAROFF_ENTRY)) { 1062 mePtr->height = mePtr->width = 0; 1063 } else { 1064 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height); 1065 mePtr->height = height + 2 * activeBorderWidth + 10; 1066 mePtr->width = width; 1067 1068 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr, 1069 &width, &height); 1070 mePtr->indicatorSpace = width; 1071 if (width > 0) { 1072 mePtr->width += width; 1073 } 1074 mePtr->width += 2 * activeBorderWidth + 10; 1075 } 1076 if (mePtr->entryFlags & ENTRY_HELP_MENU) { 1077 helpMenuIndex = i; 1078 } else if (x + mePtr->width + borderWidth > maxWindowWidth) { 1079 1080 if (i == lastRowBreak) { 1081 mePtr->y = y; 1082 mePtr->x = x; 1083 lastRowBreak++; 1084 y += mePtr->height; 1085 currentRowHeight = 0; 1086 } else { 1087 x = borderWidth; 1088 for (j = lastRowBreak; j < i; j++) { 1089 menuPtr->entries[j]->y = y + currentRowHeight 1090 - menuPtr->entries[j]->height; 1091 menuPtr->entries[j]->x = x; 1092 x += menuPtr->entries[j]->width; 1093 } 1094 lastRowBreak = i; 1095 y += currentRowHeight; 1096 currentRowHeight = mePtr->height; 1097 } 1098 if (x > maxWidth) { 1099 maxWidth = x; 1100 } 1101 x = borderWidth; 1102 } else { 1103 x += mePtr->width; 1104 if (mePtr->height > currentRowHeight) { 1105 currentRowHeight = mePtr->height; 1106 } 1107 } 1108 } 1109 1110 lastEntry = menuPtr->numEntries - 1; 1111 if (helpMenuIndex == lastEntry) { 1112 lastEntry--; 1113 } 1114 if ((lastEntry >= 0) && (x + menuPtr->entries[lastEntry]->width 1115 + borderWidth > maxWidth)) { 1116 maxWidth = x + menuPtr->entries[lastEntry]->width + borderWidth; 1117 } 1118 x = borderWidth; 1119 for (j = lastRowBreak; j < menuPtr->numEntries; j++) { 1120 if (j == helpMenuIndex) { 1121 continue; 1122 } 1123 menuPtr->entries[j]->y = y + currentRowHeight 1124 - menuPtr->entries[j]->height; 1125 menuPtr->entries[j]->x = x; 1126 x += menuPtr->entries[j]->width; 1127 } 1128 1129 1130 if (helpMenuIndex != -1) { 1131 mePtr = menuPtr->entries[helpMenuIndex]; 1132 if (x + mePtr->width + borderWidth > maxWindowWidth) { 1133 y += currentRowHeight; 1134 currentRowHeight = mePtr->height; 1135 x = borderWidth; 1136 } else if (mePtr->height > currentRowHeight) { 1137 currentRowHeight = mePtr->height; 1138 } 1139 mePtr->x = maxWindowWidth - borderWidth - mePtr->width; 1140 mePtr->y = y + currentRowHeight - mePtr->height; 1141 } 1142 height = y + currentRowHeight + borderWidth; 1143 } 1144 width = Tk_Width(menuPtr->tkwin); 1145 1146 /* 1147 * The X server doesn't like zero dimensions, so round up to at least 1 (a 1148 * zero-sized menu should never really occur, anyway). 1149 */ 1150 1151 if (width <= 0) { 1152 width = 1; 1153 } 1154 if (height <= 0) { 1155 height = 1; 1156 } 1157 menuPtr->totalWidth = maxWidth; 1158 menuPtr->totalHeight = height; 1159} 1160 1161/* 1162 *---------------------------------------------------------------------- 1163 * 1164 * DrawTearoffEntry -- 1165 * 1166 * This procedure draws the background part of a menu. 1167 * 1168 * Results: 1169 * None. 1170 * 1171 * Side effects: 1172 * Commands are output to X to display the menu in its current mode. 1173 * 1174 *---------------------------------------------------------------------- 1175 */ 1176 1177static void 1178DrawTearoffEntry( 1179 TkMenu *menuPtr, /* The menu we are drawing */ 1180 TkMenuEntry *mePtr, /* The entry we are drawing */ 1181 Drawable d, /* The drawable we are drawing into */ 1182 GC gc, /* The gc we are drawing with */ 1183 Tk_Font tkfont, /* The font we are drawing with */ 1184 CONST Tk_FontMetrics *fmPtr,/* The metrics we are drawing with */ 1185 int x, int y, 1186 int width, int height) 1187{ 1188 XPoint points[2]; 1189 int segmentWidth, maxX; 1190 Tk_3DBorder border; 1191 1192 if (menuPtr->menuType != MASTER_MENU) { 1193 return; 1194 } 1195 1196 points[0].x = x; 1197 points[0].y = y + height/2; 1198 points[1].y = points[0].y; 1199 segmentWidth = 6; 1200 maxX = width - 1; 1201 border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); 1202 1203 while (points[0].x < maxX) { 1204 points[1].x = points[0].x + segmentWidth; 1205 if (points[1].x > maxX) { 1206 points[1].x = maxX; 1207 } 1208 Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, 1209 TK_RELIEF_RAISED); 1210 points[0].x += 2 * segmentWidth; 1211 } 1212} 1213 1214/* 1215 *-------------------------------------------------------------- 1216 * 1217 * TkpInitializeMenuBindings -- 1218 * 1219 * For every interp, initializes the bindings for Windows menus. Does 1220 * nothing on Mac or XWindows. 1221 * 1222 * Results: 1223 * None. 1224 * 1225 * Side effects: 1226 * C-level bindings are setup for the interp which will handle Alt-key 1227 * sequences for menus without beeping or interfering with user-defined 1228 * Alt-key bindings. 1229 * 1230 *-------------------------------------------------------------- 1231 */ 1232 1233void 1234TkpInitializeMenuBindings( 1235 Tcl_Interp *interp, /* The interpreter to set. */ 1236 Tk_BindingTable bindingTable) 1237 /* The table to add to. */ 1238{ 1239 /* 1240 * Nothing to do. 1241 */ 1242} 1243 1244/* 1245 *---------------------------------------------------------------------- 1246 * 1247 * SetHelpMenu -- 1248 * 1249 * Given a menu, check to see whether or not it is a help menu cascade in 1250 * a menubar. If it is, the entry that points to this menu will be 1251 * marked. 1252 * 1253 * RESULTS: 1254 * None. 1255 * 1256 * Side effects: 1257 * Will set the ENTRY_HELP_MENU flag appropriately. 1258 * 1259 *---------------------------------------------------------------------- 1260 */ 1261 1262static void 1263SetHelpMenu( 1264 TkMenu *menuPtr) /* The menu we are checking */ 1265{ 1266 TkMenuEntry *cascadeEntryPtr; 1267 int useMotifHelp = 0; 1268 const char *option = NULL; 1269 if (menuPtr->tkwin) { 1270 option = Tk_GetOption(menuPtr->tkwin, "useMotifHelp", "UseMotifHelp"); 1271 if (option != NULL) { 1272 Tcl_GetBoolean(NULL, option, &useMotifHelp); 1273 } 1274 } 1275 1276 if (!useMotifHelp) { 1277 return; 1278 } 1279 1280 for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; 1281 cascadeEntryPtr != NULL; 1282 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { 1283 if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR) 1284 && (cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin != NULL) 1285 && (menuPtr->masterMenuPtr->tkwin != NULL)) { 1286 TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr; 1287 char *helpMenuName = ckalloc(strlen(Tk_PathName( 1288 masterMenuPtr->tkwin)) + strlen(".help") + 1); 1289 1290 strcpy(helpMenuName, Tk_PathName(masterMenuPtr->tkwin)); 1291 strcat(helpMenuName, ".help"); 1292 if (strcmp(helpMenuName, 1293 Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) { 1294 cascadeEntryPtr->entryFlags |= ENTRY_HELP_MENU; 1295 } else { 1296 cascadeEntryPtr->entryFlags &= ~ENTRY_HELP_MENU; 1297 } 1298 ckfree(helpMenuName); 1299 } 1300 } 1301} 1302 1303/* 1304 *---------------------------------------------------------------------- 1305 * 1306 * TkpDrawMenuEntry -- 1307 * 1308 * Draws the given menu entry at the given coordinates with the given 1309 * attributes. 1310 * 1311 * Results: 1312 * None. 1313 * 1314 * Side effects: 1315 * X Server commands are executed to display the menu entry. 1316 * 1317 *---------------------------------------------------------------------- 1318 */ 1319 1320void 1321TkpDrawMenuEntry( 1322 TkMenuEntry *mePtr, /* The entry to draw */ 1323 Drawable d, /* What to draw into */ 1324 Tk_Font tkfont, /* Precalculated font for menu */ 1325 CONST Tk_FontMetrics *menuMetricsPtr, 1326 /* Precalculated metrics for menu */ 1327 int x, /* X-coordinate of topleft of entry */ 1328 int y, /* Y-coordinate of topleft of entry */ 1329 int width, /* Width of the entry rectangle */ 1330 int height, /* Height of the current rectangle */ 1331 int strictMotif, /* Boolean flag */ 1332 int drawArrow) /* Whether or not to draw the cascade 1333 * arrow for cascade items. Only applies 1334 * to Windows. */ 1335{ 1336 GC gc, indicatorGC; 1337 XColor *indicatorColor, *disableColor = NULL; 1338 TkMenu *menuPtr = mePtr->menuPtr; 1339 Tk_3DBorder bgBorder, activeBorder; 1340 CONST Tk_FontMetrics *fmPtr; 1341 Tk_FontMetrics entryMetrics; 1342 int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0; 1343 int adjustedY = y + padY; 1344 int adjustedHeight = height - 2 * padY; 1345 1346 /* 1347 * Choose the gc for drawing the foreground part of the entry. 1348 */ 1349 1350 if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) { 1351 gc = mePtr->activeGC; 1352 if (gc == NULL) { 1353 gc = menuPtr->activeGC; 1354 } 1355 } else { 1356 TkMenuEntry *cascadeEntryPtr; 1357 int parentDisabled = 0; 1358 1359 for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; 1360 cascadeEntryPtr != NULL; 1361 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { 1362 if (cascadeEntryPtr->namePtr != NULL) { 1363 char *name = Tcl_GetString(cascadeEntryPtr->namePtr); 1364 1365 if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) { 1366 if (cascadeEntryPtr->state == ENTRY_DISABLED) { 1367 parentDisabled = 1; 1368 } 1369 break; 1370 } 1371 } 1372 } 1373 1374 if (((parentDisabled || (mePtr->state == ENTRY_DISABLED))) 1375 && (menuPtr->disabledFgPtr != NULL)) { 1376 gc = mePtr->disabledGC; 1377 if (gc == NULL) { 1378 gc = menuPtr->disabledGC; 1379 } 1380 } else { 1381 gc = mePtr->textGC; 1382 if (gc == NULL) { 1383 gc = menuPtr->textGC; 1384 } 1385 } 1386 } 1387 indicatorGC = mePtr->indicatorGC; 1388 if (indicatorGC == NULL) { 1389 indicatorGC = menuPtr->indicatorGC; 1390 } 1391 if (mePtr->indicatorFgPtr) { 1392 indicatorColor = Tk_GetColorFromObj(menuPtr->tkwin, 1393 mePtr->indicatorFgPtr); 1394 } else { 1395 indicatorColor = Tk_GetColorFromObj(menuPtr->tkwin, 1396 menuPtr->indicatorFgPtr); 1397 } 1398 1399 if (menuPtr->disabledFgPtr != NULL) { 1400 disableColor = Tk_GetColorFromObj(menuPtr->tkwin, 1401 menuPtr->disabledFgPtr); 1402 } 1403 1404 bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, 1405 (mePtr->borderPtr == NULL) 1406 ? menuPtr->borderPtr : mePtr->borderPtr); 1407 if (strictMotif) { 1408 activeBorder = bgBorder; 1409 } else { 1410 activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, 1411 (mePtr->activeBorderPtr == NULL) 1412 ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr); 1413 } 1414 1415 if (mePtr->fontPtr == NULL) { 1416 fmPtr = menuMetricsPtr; 1417 } else { 1418 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); 1419 Tk_GetFontMetrics(tkfont, &entryMetrics); 1420 fmPtr = &entryMetrics; 1421 } 1422 1423 /* 1424 * Need to draw the entire background, including padding. On Unix, for 1425 * menubars, we have to draw the rest of the entry taking into account the 1426 * padding. 1427 */ 1428 1429 DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, 1430 bgBorder, x, y, width, height); 1431 1432 if (mePtr->type == SEPARATOR_ENTRY) { 1433 DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, 1434 fmPtr, x, adjustedY, width, adjustedHeight); 1435 } else if (mePtr->type == TEAROFF_ENTRY) { 1436 DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY, 1437 width, adjustedHeight); 1438 } else { 1439 DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY, 1440 width, adjustedHeight); 1441 DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, 1442 activeBorder, x, adjustedY, width, adjustedHeight, drawArrow); 1443 if (!mePtr->hideMargin) { 1444 if (mePtr->state == ENTRY_ACTIVE) { 1445 bgBorder = activeBorder; 1446 } 1447 DrawMenuEntryIndicator(menuPtr, mePtr, d, bgBorder, indicatorColor, 1448 disableColor, tkfont, fmPtr, x, adjustedY, width, 1449 adjustedHeight); 1450 } 1451 } 1452} 1453 1454/* 1455 *---------------------------------------------------------------------- 1456 * 1457 * GetMenuLabelGeometry -- 1458 * 1459 * Figures out the size of the label portion of a menu item. 1460 * 1461 * Results: 1462 * widthPtr and heightPtr are filled in with the correct geometry 1463 * information. 1464 * 1465 * Side effects: 1466 * None. 1467 * 1468 *---------------------------------------------------------------------- 1469 */ 1470 1471static void 1472GetMenuLabelGeometry( 1473 TkMenuEntry *mePtr, /* The entry we are computing */ 1474 Tk_Font tkfont, /* The precalculated font */ 1475 CONST Tk_FontMetrics *fmPtr,/* The precalculated metrics */ 1476 int *widthPtr, /* The resulting width of the label portion */ 1477 int *heightPtr) /* The resulting height of the label 1478 * portion */ 1479{ 1480 TkMenu *menuPtr = mePtr->menuPtr; 1481 int haveImage = 0; 1482 1483 if (mePtr->image != NULL) { 1484 Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr); 1485 haveImage = 1; 1486 } else if (mePtr->bitmapPtr != NULL) { 1487 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); 1488 Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr); 1489 haveImage = 1; 1490 } else { 1491 *heightPtr = 0; 1492 *widthPtr = 0; 1493 } 1494 1495 if (haveImage && (mePtr->compound == COMPOUND_NONE)) { 1496 /* 1497 * We don't care about the text in this case. 1498 */ 1499 } else { 1500 /* 1501 * Either it is compound or we don't have an image. 1502 */ 1503 1504 if (mePtr->labelPtr != NULL) { 1505 int textWidth; 1506 char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL); 1507 textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength); 1508 1509 if ((mePtr->compound != COMPOUND_NONE) && haveImage) { 1510 switch ((enum compound) mePtr->compound) { 1511 case COMPOUND_TOP: 1512 case COMPOUND_BOTTOM: 1513 if (textWidth > *widthPtr) { 1514 *widthPtr = textWidth; 1515 } 1516 1517 /* 1518 * Add text and padding. 1519 */ 1520 1521 *heightPtr += fmPtr->linespace + 2; 1522 break; 1523 case COMPOUND_LEFT: 1524 case COMPOUND_RIGHT: 1525 if (fmPtr->linespace > *heightPtr) { 1526 *heightPtr = fmPtr->linespace; 1527 } 1528 1529 /* 1530 * Add text and padding. 1531 */ 1532 1533 *widthPtr += textWidth + 2; 1534 break; 1535 case COMPOUND_CENTER: 1536 if (fmPtr->linespace > *heightPtr) { 1537 *heightPtr = fmPtr->linespace; 1538 } 1539 if (textWidth > *widthPtr) { 1540 *widthPtr = textWidth; 1541 } 1542 break; 1543 case COMPOUND_NONE: 1544 break; 1545 } 1546 } else { 1547 /* 1548 * We don't have an image or we're not compound. 1549 */ 1550 1551 *heightPtr = fmPtr->linespace; 1552 *widthPtr = textWidth; 1553 } 1554 } else { 1555 /* 1556 * An empty entry still has this height. 1557 */ 1558 1559 *heightPtr = fmPtr->linespace; 1560 } 1561 } 1562 *heightPtr += 1; 1563} 1564 1565/* 1566 *-------------------------------------------------------------- 1567 * 1568 * TkpComputeStandardMenuGeometry -- 1569 * 1570 * This procedure is invoked to recompute the size and layout of a menu 1571 * that is not a menubar clone. 1572 * 1573 * Results: 1574 * None. 1575 * 1576 * Side effects: 1577 * Fields of menu entries are changed to reflect their current positions, 1578 * and the size of the menu window itself may be changed. 1579 * 1580 *-------------------------------------------------------------- 1581 */ 1582 1583void 1584TkpComputeStandardMenuGeometry( 1585 TkMenu *menuPtr) /* Structure describing menu. */ 1586{ 1587 Tk_Font tkfont, menuFont; 1588 Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; 1589 int x, y, height, width, indicatorSpace, labelWidth, accelWidth; 1590 int windowWidth, windowHeight, accelSpace, i, j, lastColumnBreak = 0; 1591 TkMenuEntry *mePtr; 1592 int borderWidth, activeBorderWidth; 1593 1594 if (menuPtr->tkwin == NULL) { 1595 return; 1596 } 1597 1598 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 1599 &borderWidth); 1600 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, 1601 &activeBorderWidth); 1602 x = y = borderWidth; 1603 indicatorSpace = labelWidth = accelWidth = 0; 1604 windowHeight = windowWidth = 0; 1605 1606 /* 1607 * On the Mac especially, getting font metrics can be quite slow, so we 1608 * want to do it intelligently. We are going to precalculate them and pass 1609 * them down to all of the measuring and drawing routines. We will measure 1610 * the font metrics of the menu once. If an entry does not have its own 1611 * font set, then we give the geometry/drawing routines the menu's font 1612 * and metrics. If an entry has its own font, we will measure that font 1613 * and give all of the geometry/drawing the entry's font and metrics. 1614 */ 1615 1616 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 1617 Tk_GetFontMetrics(menuFont, &menuMetrics); 1618 accelSpace = Tk_TextWidth(menuFont, "M", 1); 1619 1620 for (i = 0; i < menuPtr->numEntries; i++) { 1621 mePtr = menuPtr->entries[i]; 1622 if (mePtr->fontPtr == NULL) { 1623 tkfont = menuFont; 1624 fmPtr = &menuMetrics; 1625 } else { 1626 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); 1627 Tk_GetFontMetrics(tkfont, &entryMetrics); 1628 fmPtr = &entryMetrics; 1629 } 1630 1631 if ((i > 0) && mePtr->columnBreak) { 1632 if (accelWidth != 0) { 1633 labelWidth += accelSpace; 1634 } 1635 for (j = lastColumnBreak; j < i; j++) { 1636 menuPtr->entries[j]->indicatorSpace = indicatorSpace; 1637 menuPtr->entries[j]->labelWidth = labelWidth; 1638 menuPtr->entries[j]->width = indicatorSpace + labelWidth 1639 + accelWidth + 2 * activeBorderWidth; 1640 menuPtr->entries[j]->x = x; 1641 menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN; 1642 } 1643 x += indicatorSpace + labelWidth + accelWidth 1644 + 2 * activeBorderWidth; 1645 windowWidth = x; 1646 indicatorSpace = labelWidth = accelWidth = 0; 1647 lastColumnBreak = i; 1648 y = borderWidth; 1649 } 1650 1651 if (mePtr->type == SEPARATOR_ENTRY) { 1652 GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, fmPtr, 1653 &width, &height); 1654 mePtr->height = height; 1655 } else if (mePtr->type == TEAROFF_ENTRY) { 1656 GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, fmPtr, 1657 &width, &height); 1658 mePtr->height = height; 1659 labelWidth = width; 1660 } else { 1661 /* 1662 * For each entry, compute the height required by that particular 1663 * entry, plus three widths: the width of the label, the width to 1664 * allow for an indicator to be displayed to the left of the label 1665 * (if any), and the width of the accelerator to be displayed to 1666 * the right of the label (if any). These sizes depend, of course, 1667 * on the type of the entry. 1668 */ 1669 1670 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height); 1671 mePtr->height = height; 1672 if (!mePtr->hideMargin) { 1673 width += MENU_MARGIN_WIDTH; 1674 } 1675 if (width > labelWidth) { 1676 labelWidth = width; 1677 } 1678 1679 GetMenuAccelGeometry(menuPtr, mePtr, tkfont, 1680 fmPtr, &width, &height); 1681 if (height > mePtr->height) { 1682 mePtr->height = height; 1683 } 1684 if (!mePtr->hideMargin) { 1685 width += MENU_MARGIN_WIDTH; 1686 } 1687 if (width > accelWidth) { 1688 accelWidth = width; 1689 } 1690 1691 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, 1692 fmPtr, &width, &height); 1693 if (height > mePtr->height) { 1694 mePtr->height = height; 1695 } 1696 if (!mePtr->hideMargin) { 1697 width += MENU_MARGIN_WIDTH; 1698 } 1699 if (width > indicatorSpace) { 1700 indicatorSpace = width; 1701 } 1702 1703 mePtr->height += 2 * activeBorderWidth + MENU_DIVIDER_HEIGHT; 1704 } 1705 mePtr->y = y; 1706 y += mePtr->height; 1707 if (y > windowHeight) { 1708 windowHeight = y; 1709 } 1710 } 1711 1712 if (accelWidth != 0) { 1713 labelWidth += accelSpace; 1714 } 1715 for (j = lastColumnBreak; j < menuPtr->numEntries; j++) { 1716 menuPtr->entries[j]->indicatorSpace = indicatorSpace; 1717 menuPtr->entries[j]->labelWidth = labelWidth; 1718 menuPtr->entries[j]->width = indicatorSpace + labelWidth 1719 + accelWidth + 2 * activeBorderWidth; 1720 menuPtr->entries[j]->x = x; 1721 menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN; 1722 } 1723 windowWidth = x + indicatorSpace + labelWidth + accelWidth 1724 + 2 * activeBorderWidth + 2 * borderWidth; 1725 1726 windowHeight += borderWidth; 1727 1728 /* 1729 * The X server doesn't like zero dimensions, so round up to at least 1 (a 1730 * zero-sized menu should never really occur, anyway). 1731 */ 1732 1733 if (windowWidth <= 0) { 1734 windowWidth = 1; 1735 } 1736 if (windowHeight <= 0) { 1737 windowHeight = 1; 1738 } 1739 menuPtr->totalWidth = windowWidth; 1740 menuPtr->totalHeight = windowHeight; 1741} 1742 1743/* 1744 *---------------------------------------------------------------------- 1745 * 1746 * TkpMenuNotifyToplevelCreate -- 1747 * 1748 * This routine reconfigures the menu and the clones indicated by 1749 * menuName becuase a toplevel has been created and any system menus need 1750 * to be created. Not applicable to UNIX. 1751 * 1752 * Results: 1753 * None. 1754 * 1755 * Side effects: 1756 * An idle handler is set up to do the reconfiguration. 1757 * 1758 *---------------------------------------------------------------------- 1759 */ 1760 1761void 1762TkpMenuNotifyToplevelCreate( 1763 Tcl_Interp *interp, /* The interp the menu lives in. */ 1764 char *menuName) /* The name of the menu to reconfigure. */ 1765{ 1766 /* 1767 * Nothing to do. 1768 */ 1769} 1770 1771/* 1772 *---------------------------------------------------------------------- 1773 * 1774 * TkpMenuInit -- 1775 * 1776 * Does platform-specific initialization of menus. 1777 * 1778 * Results: 1779 * None. 1780 * 1781 * Side effects: 1782 * None. 1783 * 1784 *---------------------------------------------------------------------- 1785 */ 1786 1787void 1788TkpMenuInit(void) 1789{ 1790 /* 1791 * Nothing to do. 1792 */ 1793} 1794 1795 1796/* 1797 *---------------------------------------------------------------------- 1798 * 1799 * TkpMenuThreadInit -- 1800 * 1801 * Does platform-specific initialization of thread-specific menu state. 1802 * 1803 * Results: 1804 * None. 1805 * 1806 * Side effects: 1807 * None. 1808 * 1809 *---------------------------------------------------------------------- 1810 */ 1811 1812void 1813TkpMenuThreadInit(void) 1814{ 1815 /* 1816 * Nothing to do. 1817 */ 1818} 1819 1820/* 1821 * Local Variables: 1822 * mode: c 1823 * c-basic-offset: 4 1824 * fill-column: 78 1825 * End: 1826 */ 1827