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