1/* 2 * tkMacOSXMenu.c -- 3 * 4 * This module implements the Mac-platform specific features of menus. 5 * 6 * Copyright (c) 1996-1997 by Sun Microsystems, Inc. 7 * Copyright 2001, Apple Computer, Inc. 8 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution 11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tkMacOSXMenu.c,v 1.6.2.30 2007/11/09 06:26:56 das Exp $ 14 */ 15 16#include "tkMacOSXPrivate.h" 17#include "tkMenubutton.h" 18#include "tkMenu.h" 19#include "tkColor.h" 20#include "tkFont.h" 21#include "tkMacOSXDebug.h" 22 23/* 24#ifdef TK_MAC_DEBUG 25#define TK_MAC_DEBUG_MENUS 26#endif 27*/ 28 29#define USE_TK_MDEF 30 31typedef struct MacMenu { 32 MenuRef menuHdl; /* The Menu Manager data structure. */ 33#ifdef USE_TK_MDEF 34 int useMDEF; /* true if this menu uses the MDEF */ 35#endif 36} MacMenu; 37 38typedef struct MenuEntryUserData { 39 Drawable mdefDrawable; 40 TkMenuEntry *mePtr; 41 Tk_Font tkfont; 42 Tk_FontMetrics *fmPtr; 43} MenuEntryUserData; 44 45/* 46 * Platform specific flags for menu entries 47 * 48 * ENTRY_COMMAND_ACCEL Indicates the entry has the command key 49 * in its accelerator string. 50 * ENTRY_OPTION_ACCEL Indicates the entry has the option key 51 * in its accelerator string. 52 * ENTRY_SHIFT_ACCEL Indicates the entry has the shift key 53 * in its accelerator string. 54 * ENTRY_CONTROL_ACCEL Indicates the entry has the control key 55 * in its accelerator string. 56 */ 57 58#define ENTRY_COMMAND_ACCEL ENTRY_PLATFORM_FLAG1 59#define ENTRY_OPTION_ACCEL ENTRY_PLATFORM_FLAG2 60#define ENTRY_SHIFT_ACCEL ENTRY_PLATFORM_FLAG3 61#define ENTRY_CONTROL_ACCEL ENTRY_PLATFORM_FLAG4 62#define ENTRY_ACCEL_MASK (ENTRY_COMMAND_ACCEL | ENTRY_OPTION_ACCEL \ 63 | ENTRY_SHIFT_ACCEL | ENTRY_CONTROL_ACCEL) 64#define MODIFIER_NUM 4 65 66/* 67 * This structure is used to keep track of subfields within Macintosh menu 68 * items. 69 */ 70 71typedef struct EntryGeometry { 72 int accelTextStart; /* Offset into the accel string where 73 * the text starts. Everything before 74 * this is modifier key descriptions. 75 */ 76 int modifierWidth; /* Width of modifier symbols. */ 77 int accelTextWidth; /* Width of the text after the modifier 78 * keys. */ 79 int nonAccelMargin; /* The width of the margin for entries 80 * without accelerators. */ 81 int modifierNum; /* Number of modifiers */ 82 Tcl_UniChar modifierUniChars[MODIFIER_NUM]; 83 /* Modifiers in unicode */ 84 char accelGlyph; /* Accelerator glyph, if any */ 85} EntryGeometry; 86 87/* 88 * Structure to keep track of toplevel windows and their menubars. 89 */ 90 91typedef struct TopLevelMenubarList { 92 struct TopLevelMenubarList *nextPtr; 93 /* The next window in the list. */ 94 Tk_Window tkwin; /* The toplevel window. */ 95 TkMenu *menuPtr; /* The menu associated with this 96 * toplevel. */ 97} TopLevelMenubarList; 98 99/* 100 * Platform-specific flags for menus. 101 * 102 * MENU_APPLE_MENU 0 indicates a custom Apple menu has 103 * not been installed; 1 a custom Apple 104 * menu has been installed. 105 * MENU_HELP_MENU 0 indicates a custom Help menu has 106 * not been installed; 1 a custom Help 107 * menu has been installed. 108 * MENU_RECONFIGURE_PENDING 1 indicates that an idle handler has 109 * been scheduled to reconfigure the 110 * Macintosh MenuHandle. 111 */ 112 113#define MENU_APPLE_MENU MENU_PLATFORM_FLAG1 114#define MENU_HELP_MENU MENU_PLATFORM_FLAG2 115#define MENU_RECONFIGURE_PENDING MENU_PLATFORM_FLAG3 116 117#define CASCADE_CMD (0x1b) /* The special command char for cascade 118 * menus. */ 119#define MENUBAR_REDRAW_PENDING 1 120 121static int gNoTkMenus = 0; /* This is used by Tk_MacOSXTurnOffMenus as the 122 * flag that Tk is not to draw any menus. */ 123 124static Tcl_HashTable commandTable; 125 /* The list of menuInstancePtrs associated with 126 * menu ids */ 127static short currentAppleMenuID; 128 /* The id of the current Apple menu. 0 for 129 * none. */ 130static short currentHelpMenuID; /* The id of the current Help menu. 0 for 131 * none. */ 132static Tcl_Interp *currentMenuBarInterp; 133 /* The interpreter of the window that owns 134 * the current menubar. */ 135static char *currentMenuBarName; 136 /* Malloced. Name of current menu in menu bar. 137 * NULL if no menu set. TO DO: make this a 138 * DString. */ 139static Tk_Window currentMenuBarOwner; 140 /* Which window owns the current menu bar. */ 141static int inPostMenu; /* We cannot be re-entrant like X 142 * windows. */ 143static short lastMenuID; /* To pass to NewMenu; need to figure out 144 * a good way to do this. */ 145static short lastCascadeID; 146 /* Cascades have to have ids that are 147 * less than 256. */ 148static int menuBarFlags; /* Used for whether the menu bar needs 149 * redrawing or not. */ 150 151struct MenuCommandHandlerData { /* This is the ClientData we pass to */ 152 TkMenu *menuPtr; /* Tcl_DoWhenIdle to move handling */ 153 int index; /* menu commands to the event loop. */ 154}; 155 156static TopLevelMenubarList *windowListPtr; 157 /* A list of windows that have menubars set. */ 158 159/* 160 * Array of unicode, charcode and utf representations of the most common 161 * special menu symbols. 162 */ 163typedef struct MenuSymbol { 164 const Tcl_UniChar unicode; 165 const char charCode; 166 /* char padding; */ 167 int utfLen, width; 168 char utf[TCL_UTF_MAX + 1]; 169} MenuSymbol; 170 171static MenuSymbol menuSymbols[] = { 172 {kCommandUnicode, kCommandCharCode}, 173 {kOptionUnicode, kMenuOptionGlyph}, 174 {kControlUnicode, kMenuControlGlyph}, 175 {kShiftUnicode, kMenuShiftGlyph}, 176 {kCheckUnicode, kCheckCharCode}, 177 {kDiamondUnicode, kDiamondCharCode}, 178 {kBulletUnicode, kBulletCharCode}, 179 {0x2026, kNullCharCode}, 180 {0, 0}, 181}; 182 183enum MenuSymbolIdx { 184 COMMAND_SYMBOL, 185 OPTION_SYMBOL, 186 CONTROL_SYMBOL, 187 SHIFT_SYMBOL, 188 CHECK_SYMBOL, 189 DIAMDOND_SYMBOL, 190 BULLET_SYMBOL, 191 ELLIPSIS_SYMBOL, 192}; 193 194MenuRef tkCurrentAppleMenu = NULL; 195 196static SInt32 menuMarkColumnWidth = 0, menuMarkIndent = 0; 197static SInt32 menuTextLeadingEdgeMargin = 0, menuTextTrailingEdgeMargin = 0; 198static SInt16 menuItemExtraHeight = 0, menuItemExtraWidth = 0; 199static SInt16 menuSeparatorHeight = 0; 200 201/* 202 * Forward declarations for procedures defined later in this file: 203 */ 204 205MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr, 206 int cascade, short *menuIDPtr); 207MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID); 208 209static void CompleteIdlers(TkMenu *menuPtr); 210static void DrawMenuBarWhenIdle(ClientData clientData); 211static void DrawMenuEntryAccelerator(TkMenu *menuPtr, TkMenuEntry *mePtr, 212 Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, 213 Tk_3DBorder activeBorder, int x, int y, int width, int height, 214 int drawArrow); 215static void DrawMenuEntryBackground(TkMenu *menuPtr, TkMenuEntry *mePtr, 216 Drawable d, Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, int x, 217 int y, int width, int heigth); 218static void DrawMenuEntryIndicator(TkMenu *menuPtr, TkMenuEntry *mePtr, 219 Drawable d, GC gc, GC indicatorGC, Tk_Font tkfont, 220 const Tk_FontMetrics *fmPtr, int x, int y, int width, int height); 221static void DrawMenuEntryLabel(TkMenu * menuPtr, TkMenuEntry *mePtr, 222 Drawable d, GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, 223 int y, int width, int height); 224static void DrawMenuSeparator(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, 225 GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, 226 int width, int height); 227static void DrawTearoffEntry(TkMenu *menuPtr, TkMenuEntry *mePtr, Drawable d, 228 GC gc, Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int x, int y, 229 int width, int height); 230static void EventuallyInvokeMenu(ClientData data); 231static void GetEntryText(TkMenuEntry *mePtr, Tcl_DString *dStringPtr); 232static void GetMenuAccelGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, 233 Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *modWidthPtr, 234 int *textWidthPtr, int *heightPtr); 235static void GetMenuLabelGeometry(TkMenuEntry *mePtr, Tk_Font tkfont, 236 const Tk_FontMetrics *fmPtr, int *widthPtr, int *heightPtr); 237static void GetMenuIndicatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, 238 Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, 239 int *heightPtr); 240static void GetMenuSeparatorGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, 241 Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, 242 int *heightPtr); 243static TkMenuEntry* GetParentMenuEntry(TkMenu *menuPtr); 244static void GetTearoffEntryGeometry(TkMenu *menuPtr, TkMenuEntry *mePtr, 245 Tk_Font tkfont, const Tk_FontMetrics *fmPtr, int *widthPtr, 246 int *heightPtr); 247static char FindMarkCharacter(TkMenuEntry *mePtr); 248static int GetUtfMarkCharacter(char markChar, const char **markUtfPtr); 249static TkMenu* MenuPtrForMenuRef(MenuRef menu); 250static int ParseAccelerators(const char **accelStringPtr, int *modifierNumPtr, 251 Tcl_UniChar *modifierUniChars, int *modifierWidth); 252static void MenuSelectEvent(TkMenu *menuPtr); 253static void ReconfigureIndividualMenu(TkMenu *menuPtr, MenuHandle macMenuHdl, 254 int base); 255static void ReconfigureMacintoshMenu(ClientData clientData); 256static void RecursivelyClearActiveMenu(TkMenu *menuPtr); 257static void RecursivelyDeleteMenu(TkMenu *menuPtr); 258static void RecursivelyInsertMenu(TkMenu *menuPtr); 259static void SetDefaultMenubar(void); 260static int SetMenuCascade(TkMenu *menuPtr); 261 262#ifdef USE_TK_MDEF 263#define SCREEN_MARGIN 5 264static MacDrawable macMDEFDrawable; 265 /* Drawable for use by MDEF code */ 266static int MDEFScrollFlag = 0; /* Used so that popups don't scroll too soon.*/ 267static MenuItemDrawingUPP tkThemeMenuItemDrawingUPP; 268 /* Points to the UPP for theme Item drawing. */ 269static Tcl_Obj *useMDEFVar; 270 271static void DrawMenuBackground(TkMenu *menuPtr, Rect *menuRectPtr, 272 Drawable d); 273static void MenuDefProc(short message, MenuHandle menu, Rect *menuRectPtr, 274 Point hitPt, short *whichItem ); 275static void HandleMenuHiliteMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt, 276 SInt16 *whichItem, TkMenu *menuPtr); 277static void HandleMenuDrawMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt, 278 SInt16 *whichItem, TkMenu *menuPtr); 279static void HandleMenuFindItemMsg(MenuRef menu, Rect *menuRectPtr, 280 Point hitPt, SInt16 *whichItem, TkMenu *menuPtr); 281static void HandleMenuPopUpMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt, 282 SInt16 *whichItem, TkMenu *menuPtr); 283static void HandleMenuCalcItemMsg(MenuRef menu, Rect *menuRectPtr, Point hitPt, 284 SInt16 *whichItem, TkMenu *menuPtr); 285static void AppearanceEntryDrawWrapper(TkMenuEntry *mePtr, Rect * menuRectPtr, 286 MenuTrackingData *mtdPtr, Drawable d, Tk_FontMetrics *fmPtr, 287 Tk_Font tkfont, int erase); 288static pascal void ThemeMenuItemDrawingProc(const Rect *inBounds, 289 SInt16 inDepth, Boolean inIsColorDevice, SInt32 inUserData); 290#else /* USE_TK_MDEF */ 291# define useMDEF 0 292#endif /* USE_TK_MDEF */ 293 294#define IS_THEME_MENU_FONT(tkfont) (strcmp(Tk_NameOfFont(tkfont), "menu") == 0) 295 296 297/* 298 *---------------------------------------------------------------------- 299 * 300 * DrawThemeText -- 301 * 302 * Wrapper for DrawThemeTextBox API. 303 * 304 * Results: 305 * None. 306 * 307 * Side effects: 308 * None. 309 * 310 *---------------------------------------------------------------------- 311 */ 312 313static void 314DrawThemeText( 315 Drawable d, 316 GC gc, 317 CFStringRef string, 318 ThemeFontID font, 319 ThemeDrawState drawState, 320 const Rect* bounds, 321 int baseline, 322 int just) 323{ 324 TkMacOSXDrawingContext dc; 325 Rect adjustedBounds; 326 327 /* 328 * Menu item text drawn with the .Keyboard font (used for 329 * kThemeMenuItemCmdKeyFont) won't always have the same ascent and 330 * baseline as text drawn with the regular menu item font, since the 331 * glyphs in the .Keyboard font may have a different height. Therefore, we 332 * first determine the baseline of the text and then adjust the bounds 333 * rect so the baseline aligns with the overall baseline of the menu item. 334 */ 335 if (font == kThemeMenuItemCmdKeyFont) { 336 Point size; 337 SInt16 cmdKeyBaseline; 338 339 GetThemeTextDimensions(string, font, drawState, false, &size, 340 &cmdKeyBaseline); 341 adjustedBounds = *bounds; 342 OffsetRect(&adjustedBounds, 0, baseline - bounds->top - size.v - 343 cmdKeyBaseline); 344 bounds = &adjustedBounds; 345 } 346 if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { 347 ChkErr(DrawThemeTextBox, string, font, drawState, false, bounds, just, 348 dc.context); 349 TkMacOSXRestoreDrawingContext(&dc); 350 } 351} 352 353/* 354 *---------------------------------------------------------------------- 355 * 356 * MeasureThemeText -- 357 * 358 * Wrapper for GetThemeTextDimensions API. 359 * 360 * Results: 361 * None. 362 * 363 * Side effects: 364 * None. 365 * 366 *---------------------------------------------------------------------- 367 */ 368 369static int 370MeasureThemeText( 371 CFStringRef string, 372 ThemeFontID font) 373{ 374 Point pt; 375 376 ChkErr(GetThemeTextDimensions, string, font, kThemeStateActive, false, &pt, 377 NULL); 378 return pt.h; 379} 380 381/* 382 *---------------------------------------------------------------------- 383 * 384 * TkMacOSXUseID -- 385 * 386 * Take the ID out of the available list for new menus. Used by the 387 * default menu bar's menus so that they do not get created at the tk 388 * level. See TkMacOSXGetNewMenuID for more information. 389 * 390 * Results: 391 * Returns TCL_OK if the id was not in use. Returns TCL_ERROR if the 392 * id was in use. 393 * 394 * Side effects: 395 * A hash table entry in the command table is created with a NULL 396 * value. 397 * 398 *---------------------------------------------------------------------- 399 */ 400 401int 402TkMacOSXUseMenuID( 403 short macID) /* The id to take out of the table */ 404{ 405 Tcl_HashEntry *commandEntryPtr; 406 int newEntry; 407 int iMacID = macID; /* Do this to remove compiler warning */ 408 409 TkMenuInit(); 410 commandEntryPtr = Tcl_CreateHashEntry(&commandTable, (char *) iMacID, 411 &newEntry); 412 if (!newEntry) { 413 return TCL_ERROR; 414 } 415 Tcl_SetHashValue(commandEntryPtr, NULL); 416 return TCL_OK; 417} 418 419/* 420 *---------------------------------------------------------------------- 421 * 422 * TkMacOSXGetNewMenuID -- 423 * 424 * Allocates a new menu id and marks it in use. Each menu on the 425 * mac must be designated by a unique id, which is a short. In 426 * addition, some ids are reserved by the system. Since Tk uses 427 * mostly dynamic menus, we must allocate and free these ids on 428 * the fly. We use the id as a key into a hash table; if there 429 * is no hash entry, we know that we can use the id. 430 * 431 * Carbon allows a much larger number of menus than the old APIs. 432 * I believe this is 32768, but am not sure. This code just uses 433 * 2000 as the upper limit. Unfortunately tk leaks menus when 434 * cloning, under some circumstances (see bug on sourceforge). 435 * 436 * Results: 437 * Returns TCL_OK if succesful; TCL_ERROR if there are no more 438 * ids of the appropriate type to allocate. menuIDPtr contains 439 * the new id if succesful. 440 * 441 * Side effects: 442 * An entry is created for the menu in the command hash table, 443 * and the hash entry is stored in the appropriate field in the 444 * menu data structure. 445 * 446 *---------------------------------------------------------------------- 447 */ 448 449int 450TkMacOSXGetNewMenuID( 451 Tcl_Interp *interp, /* Used for error reporting */ 452 TkMenu *menuPtr, /* The menu we are working with */ 453 int cascade, /* 0 if we are working with a normal menu; 454 * 1 if we are working with a cascade */ 455 short *menuIDPtr) /* The resulting id */ 456{ 457 int found = 0; 458 int newEntry; 459 Tcl_HashEntry *commandEntryPtr = NULL; 460 short returnID = *menuIDPtr; 461 462 /* 463 * The following code relies on shorts and unsigned chars wrapping 464 * when the highest value is incremented. Also, the values between 465 * 236 and 255 inclusive are reserved for DA's by the Mac OS. 466 */ 467 468 if (!cascade) { 469 short curID = lastMenuID + 1; 470 471 if (curID == 236) { 472 curID = 256; 473 } 474 475 while (curID != lastMenuID) { 476 int iCurID = curID; 477 commandEntryPtr = Tcl_CreateHashEntry(&commandTable, 478 (char *) iCurID, &newEntry); 479 if (newEntry == 1) { 480 found = 1; 481 lastMenuID = returnID = curID; 482 break; 483 } 484 curID++; 485 if (curID == 236) { 486 curID = 256; 487 } 488 } 489 } else { 490 /* 491 * Cascade ids must be between 0 and 235 only, so they must be 492 * dealt with separately. 493 */ 494 495 short curID = lastCascadeID + 1; 496 497 if (curID == 2000) { 498 curID = 0; 499 } 500 501 while (curID != lastCascadeID) { 502 int iCurID = curID; 503 commandEntryPtr = Tcl_CreateHashEntry(&commandTable, 504 (char *) iCurID, &newEntry); 505 if (newEntry == 1) { 506 found = 1; 507 lastCascadeID = returnID = curID; 508 break; 509 } 510 curID++; 511 if (curID == 2000) { 512 curID = 0; 513 } 514 } 515 } 516 517 if (!found) { 518 Tcl_ResetResult(interp); 519 Tcl_AppendResult(interp, "No more menus can be allocated.", NULL); 520 return TCL_ERROR; 521 } 522 Tcl_SetHashValue(commandEntryPtr, (char *) menuPtr); 523 *menuIDPtr = returnID; 524 return TCL_OK; 525} 526 527/* 528 *---------------------------------------------------------------------- 529 * 530 * TkMacOSXFreeMenuID -- 531 * 532 * Marks the id as free. 533 * 534 * Results: 535 * None. 536 * 537 * Side effects: 538 * The hash table entry for the ID is cleared. 539 * 540 *---------------------------------------------------------------------- 541 */ 542 543void 544TkMacOSXFreeMenuID( 545 short menuID) /* The id to free */ 546{ 547 Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&commandTable, 548 (char*)(intptr_t)menuID); 549 550 if (entryPtr != NULL) { 551 Tcl_DeleteHashEntry(entryPtr); 552 } 553 if (menuID == currentAppleMenuID) { 554 currentAppleMenuID = 0; 555 } 556 if (menuID == currentHelpMenuID) { 557 currentHelpMenuID = 0; 558 } 559} 560 561/* 562 *---------------------------------------------------------------------- 563 * 564 * MenuPtrForMenuRef -- 565 * 566 * Returns a pointer to the TkMenu corresponding to a given 567 * Carbon MenuRef. 568 * 569 * Results: 570 * Returns a pointer to a TkMenu or NULL. 571 * 572 * Side effects: 573 * None. 574 * 575 *---------------------------------------------------------------------- 576 */ 577 578TkMenu* 579MenuPtrForMenuRef( 580 MenuRef menu) 581{ 582 TkMenu *menuPtr = NULL; 583 MenuID menuID = GetMenuID(menu); 584 Tcl_HashEntry *commandEntryPtr = Tcl_FindHashEntry(&commandTable, 585 (char*)(intptr_t)menuID); 586 587 if (commandEntryPtr) { 588 menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr); 589 } 590 return menuPtr; 591} 592 593/* 594 *---------------------------------------------------------------------- 595 * 596 * GetParentMenuEntry -- 597 * 598 * Returns a pointer to the parent's TkMenuEntry of a given TkMenu. 599 * 600 * Results: 601 * Returns a pointer to a TkMenuEntry or NULL. 602 * 603 * Side effects: 604 * None. 605 * 606 *---------------------------------------------------------------------- 607 */ 608 609TkMenuEntry* 610GetParentMenuEntry( 611 TkMenu *menuPtr) 612{ 613 TkMenuEntry *cascadeEntryPtr; 614 615 for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; 616 cascadeEntryPtr != NULL; 617 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { 618 const char *name = (cascadeEntryPtr->namePtr == NULL) ? "" 619 : Tcl_GetString(cascadeEntryPtr->namePtr); 620 621 if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) { 622 break; 623 } 624 } 625 return cascadeEntryPtr; 626} 627 628/* 629 *---------------------------------------------------------------------- 630 * 631 * TkpNewMenu -- 632 * 633 * Gets a new blank menu. Only the platform specific options are filled 634 * in. 635 * 636 * Results: 637 * Returns a standard TCL error. 638 * 639 * Side effects: 640 * Allocates a Macintosh menu handle and puts in the platformData 641 * field of the menuPtr. 642 * 643 *---------------------------------------------------------------------- 644 */ 645 646int 647TkpNewMenu( 648 TkMenu *menuPtr) /* The common structure we are making the 649 * platform structure for. */ 650{ 651 short menuID; 652 MenuRef macMenuHdl; 653#ifdef USE_TK_MDEF 654 MenuDefSpec menuDefSpec; 655 Tcl_Obj *useMDEFObjPtr; 656 int useMDEF = 1; 657#endif 658 int error = TCL_OK; 659 OSStatus err; 660 CFStringRef cfStr; 661 662 error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 0, &menuID); 663 if (error != TCL_OK) { 664 return error; 665 } 666 err = ChkErr(CreateNewMenu, menuID, kMenuAttrDoNotUseUserCommandKeys, 667 &macMenuHdl); 668 if (err != noErr) { 669 Tcl_AppendResult(menuPtr->interp, "CreateNewMenu failed.", NULL); 670 return TCL_ERROR; 671 } 672 cfStr = CFStringCreateWithCString(NULL, Tk_PathName(menuPtr->tkwin), 673 kCFStringEncodingUTF8); 674 if (!cfStr) { 675 Tcl_AppendResult(menuPtr->interp, "CFStringCreateWithCString failed.", 676 NULL); 677 return TCL_ERROR; 678 } 679 err = ChkErr(SetMenuTitleWithCFString, macMenuHdl, cfStr); 680 CFRelease(cfStr); 681 if (err != noErr) { 682 Tcl_AppendResult(menuPtr->interp, "SetMenuTitleWithCFString failed.", 683 NULL); 684 return TCL_ERROR; 685 } 686 687 menuPtr->platformData = (TkMenuPlatformData) ckalloc(sizeof(MacMenu)); 688 ((MacMenu *) menuPtr->platformData)->menuHdl = macMenuHdl; 689 690#ifdef USE_TK_MDEF 691 /* 692 * Check whether we want to use the custom mdef or not. For now 693 * the default is to use it unless the variable is explicitly 694 * set to no. 695 */ 696 697 useMDEFObjPtr = Tcl_ObjGetVar2(menuPtr->interp, useMDEFVar, NULL, 698 TCL_GLOBAL_ONLY); 699 if (useMDEFObjPtr == NULL || Tcl_GetBooleanFromObj(NULL, useMDEFObjPtr, 700 &useMDEF) == TCL_ERROR || useMDEF) { 701 menuDefSpec.defType = kMenuDefProcPtr; 702 menuDefSpec.u.defProc = MenuDefProc; 703 ChkErr(SetMenuDefinition, macMenuHdl, &menuDefSpec); 704 } 705 ((MacMenu *) menuPtr->platformData)->useMDEF = useMDEF; 706#endif /* USE_TK_MDEF */ 707 708 if ((currentMenuBarInterp == menuPtr->interp) 709 && (currentMenuBarName != NULL)) { 710 Tk_Window parentWin = Tk_Parent(menuPtr->tkwin); 711 712 if (strcmp(currentMenuBarName, Tk_PathName(parentWin)) == 0) { 713 if ((strcmp(Tk_PathName(menuPtr->tkwin) 714 + strlen(Tk_PathName(parentWin)), ".apple") == 0) 715 || (strcmp(Tk_PathName(menuPtr->tkwin) 716 + strlen(Tk_PathName(parentWin)), ".help") == 0)) { 717 if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { 718 Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); 719 menuBarFlags |= MENUBAR_REDRAW_PENDING; 720 } 721 } 722 } 723 } 724 725 menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 726 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr); 727 return TCL_OK; 728} 729 730/* 731 *---------------------------------------------------------------------- 732 * 733 * TkpDestroyMenu -- 734 * 735 * Destroys platform-specific menu structures. 736 * 737 * Results: 738 * None. 739 * 740 * Side effects: 741 * All platform-specific allocations are freed up. 742 * 743 *---------------------------------------------------------------------- 744 */ 745 746void 747TkpDestroyMenu( 748 TkMenu *menuPtr) /* The common menu structure */ 749{ 750 MenuRef macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; 751 752 if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) { 753 Tcl_CancelIdleCall(ReconfigureMacintoshMenu, (ClientData) menuPtr); 754 menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING; 755 } 756 if (GetMenuID(macMenuHdl) == currentHelpMenuID) { 757 MenuRef helpMenuHdl; 758 MenuItemIndex helpIndex; 759 760 if ((HMGetHelpMenu(&helpMenuHdl,&helpIndex) == noErr) 761 && (helpMenuHdl != NULL)) { 762 int i, count = CountMenuItems(helpMenuHdl); 763 764 for (i = helpIndex; i <= count; i++) { 765 DeleteMenuItem(helpMenuHdl, helpIndex); 766 } 767 } 768 currentHelpMenuID = 0; 769 } 770 if (menuPtr->platformData != NULL) { 771 MenuID menuID = GetMenuID(macMenuHdl); 772 773 DeleteMenu(menuID); 774 TkMacOSXFreeMenuID(menuID); 775 DisposeMenu(macMenuHdl); 776 ckfree((char *) menuPtr->platformData); 777 menuPtr->platformData = NULL; 778 } 779} 780 781/* 782 *---------------------------------------------------------------------- 783 * 784 * SetMenuCascade -- 785 * 786 * Does any cleanup to change a menu from a normal to a cascade. 787 * 788 * Results: 789 * Standard Tcl error. 790 * 791 * Side effects: 792 * The mac menu id is reset. 793 * 794 *---------------------------------------------------------------------- 795 */ 796 797int 798SetMenuCascade( 799 TkMenu* menuPtr) /* The menu we are setting up to be a 800 * cascade. */ 801{ 802 MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; 803 MenuID newMenuID, menuID = GetMenuID(macMenuHdl); 804 int error = TCL_OK; 805 806 if (menuID >= 256) { 807 error = TkMacOSXGetNewMenuID(menuPtr->interp, menuPtr, 1, &newMenuID); 808 if (error == TCL_OK) { 809 TkMacOSXFreeMenuID(menuID); 810 SetMenuID(macMenuHdl,newMenuID); 811 } 812 } 813 return error; 814} 815 816/* 817 *---------------------------------------------------------------------- 818 * 819 * TkpDestroyMenuEntry -- 820 * 821 * Cleans up platform-specific menu entry items. 822 * 823 * Results: 824 * None 825 * 826 * Side effects: 827 * All platform-specific allocations are freed up. 828 * 829 *---------------------------------------------------------------------- 830 */ 831 832void 833TkpDestroyMenuEntry( 834 TkMenuEntry *mePtr) /* The common structure for the menu entry. */ 835{ 836 TkMenu *menuPtr = mePtr->menuPtr; 837 838 ckfree((char *) mePtr->platformEntryData); 839 if ((menuPtr->platformData != NULL) 840 && !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { 841 menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 842 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr); 843 } 844} 845 846/* 847 *---------------------------------------------------------------------- 848 * 849 * GetEntryText -- 850 * 851 * Given a menu entry, gives back the text that should go in it. 852 * Separators should be done by the caller, as they have to be 853 * handled specially. This is primarily used to do a substitution 854 * between "..." and the ellipsis character which looks nicer. 855 * 856 * Results: 857 * itemText points to the new text for the item. 858 * 859 * Side effects: 860 * None. 861 * 862 *---------------------------------------------------------------------- 863 */ 864 865void 866GetEntryText( 867 TkMenuEntry *mePtr, /* A pointer to the menu entry. */ 868 Tcl_DString *dStringPtr) /* The DString to put the text into. This 869 * will be initialized by this routine. */ 870{ 871#ifdef USE_TK_MDEF 872 const int useMDEF = ((MacMenu *) mePtr->menuPtr->platformData)->useMDEF; 873#endif 874 int noLabel = (mePtr->labelPtr == NULL || mePtr->labelLength == 0); 875 876 Tcl_DStringInit(dStringPtr); 877 if (mePtr->type == TEAROFF_ENTRY && (useMDEF || noLabel)) { 878 Tcl_DStringAppend(dStringPtr, "(Tear-off)", -1); 879 } else if (mePtr->imagePtr != NULL && (useMDEF || noLabel) && 880 mePtr->compound == COMPOUND_NONE) { 881 Tcl_DStringAppend(dStringPtr, "(Image)", -1); 882 } else if (mePtr->bitmapPtr != NULL && (useMDEF || noLabel) && 883 mePtr->compound == COMPOUND_NONE) { 884 Tcl_DStringAppend(dStringPtr, "(Pixmap)", -1); 885 } else if (noLabel) { 886 /* 887 * The Mac menu manager does not like null strings. 888 */ 889 890 Tcl_DStringAppend(dStringPtr, " ", -1); 891 } else { 892 int length; 893 char *text = Tcl_GetStringFromObj(mePtr->labelPtr, &length); 894 char *dStringText; 895 int i; 896 897 for (i = 0; *text; text++, i++) { 898 if ((*text == '.') && (*(text+1) == '.') && (*(text+2) == '.')) { 899 Tcl_DStringAppend(dStringPtr, menuSymbols[ELLIPSIS_SYMBOL].utf, 900 menuSymbols[ELLIPSIS_SYMBOL].utfLen); 901 i += menuSymbols[ELLIPSIS_SYMBOL].utfLen - 1; 902 text += 2; 903 } else { 904 Tcl_DStringSetLength(dStringPtr, 905 Tcl_DStringLength(dStringPtr) + 1); 906 dStringText = Tcl_DStringValue(dStringPtr); 907 dStringText[i] = *text; 908 } 909 } 910 } 911} 912 913/* 914 *---------------------------------------------------------------------- 915 * 916 * FindMarkCharacter -- 917 * 918 * Finds the Macintosh mark character based on the font of the 919 * item. We calculate a good mark character based on the font 920 * that this item is rendered in. 921 * 922 * Results: 923 * Mark char. 924 * 925 * Side effects: 926 * None. 927 * 928 *---------------------------------------------------------------------- 929 */ 930 931char 932FindMarkCharacter( 933 TkMenuEntry *mePtr) /* The entry we are finding the character 934 * for. */ 935{ 936 static const char markChars[] = {kCheckCharCode, kDiamondCharCode, 937 kBulletCharCode, '-', kCheckCharCode}; 938 const char *markChar = markChars; 939 int i = sizeof(markChars); 940 Tk_Font tkfont; 941 942 tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin, 943 (mePtr->fontPtr == NULL) ? mePtr->menuPtr->fontPtr 944 : mePtr->fontPtr); 945 946 while (--i) { 947 if (!TkMacOSXIsCharacterMissing(tkfont, *markChar)) { 948 break; 949 } 950 markChar++; 951 } 952 return *markChar; 953} 954 955/* 956 *---------------------------------------------------------------------- 957 * 958 * GetUtfMarkCharacter -- 959 * 960 * Get the utf8 string for the given mark character, taking into 961 * account the special menu font char codes. 962 * 963 * Results: 964 * Length of returned utf8 string. 965 * 966 * Side effects: 967 * None. 968 * 969 *---------------------------------------------------------------------- 970 */ 971 972int 973GetUtfMarkCharacter( 974 char markChar, 975 const char **markUtfPtr) 976{ 977 const MenuSymbol *ms = menuSymbols; 978 int len = 0; 979 980 while (ms->unicode) { 981 if (ms->charCode && ms->charCode == markChar) { 982 *markUtfPtr = ms->utf; 983 len = ms->utfLen; 984 break; 985 } 986 ms++; 987 } 988 if (!len) { 989 static char markUtf[TCL_UTF_MAX + 1]; 990 991 Tcl_ExternalToUtf(NULL, TkMacOSXCarbonEncoding, &markChar, 1, 0, NULL, 992 markUtf, TCL_UTF_MAX + 1, NULL, &len, NULL); 993 *markUtfPtr = markUtf; 994 } 995 return len; 996} 997 998/* 999 *---------------------------------------------------------------------- 1000 * 1001 * ParseAccelerators -- 1002 * 1003 * Parse menu accelerator string. 1004 * 1005 * Results: 1006 * Accelerator flags. 1007 * 1008 * Side effects: 1009 * None. 1010 * 1011 *---------------------------------------------------------------------- 1012 */ 1013 1014int 1015ParseAccelerators( 1016 const char **accelStringPtr, 1017 int *modifierNumPtr, 1018 Tcl_UniChar *modifierUniChars, 1019 int *modifierWidth) 1020{ 1021 struct Modif { 1022 const char *name; 1023 const size_t len; 1024 const int flag, symbol; 1025 }; 1026#define MODIF(n, f) { #n, sizeof(#n)-1, ENTRY_##f##_ACCEL, f##_SYMBOL } 1027 static const struct Modif modifs[] = { 1028 MODIF(Control, CONTROL), 1029 MODIF(Ctrl, CONTROL), 1030 MODIF(Option, OPTION), 1031 MODIF(Opt, OPTION), 1032 MODIF(Alt, OPTION), 1033 MODIF(Shift, SHIFT), 1034 MODIF(Command, COMMAND), 1035 MODIF(Cmd, COMMAND), 1036 MODIF(Meta, COMMAND), 1037 { NULL, 0, 0, 0} 1038 }; 1039#undef MODIF 1040 const char *accelString = *accelStringPtr; 1041 int flags = 0, num = 0, seen = 0, width = 0; 1042 const struct Modif *m; 1043 1044 while (1) { 1045 m = modifs; 1046 while (m->name) { 1047 int l = m->len; 1048 1049 if (!strncasecmp(accelString, m->name, l) && 1050 (accelString[l] == '-' || accelString[l] == '+')) { 1051 flags |= m->flag; 1052 accelString += l+1; 1053 break; 1054 } 1055 m++; 1056 } 1057 if (!m->name || !*accelString) { 1058 break; 1059 } 1060 } 1061 m = modifs; 1062 while (m->name && num < MODIFIER_NUM) { 1063 if (flags & m->flag && !(seen & m->flag)) { 1064 modifierUniChars[num++] = menuSymbols[m->symbol].unicode; 1065 width += menuSymbols[m->symbol].width; 1066 seen |= m->flag; 1067 } 1068 m++; 1069 } 1070 *accelStringPtr = accelString; 1071 *modifierNumPtr = num; 1072 *modifierWidth = width; 1073 return flags; 1074} 1075 1076/* 1077 *---------------------------------------------------------------------- 1078 * 1079 * TkpConfigureMenuEntry -- 1080 * 1081 * Processes configurations for menu entries. 1082 * 1083 * Results: 1084 * Returns standard TCL result. If TCL_ERROR is returned, then 1085 * the interp's result contains an error message. 1086 * 1087 * Side effects: 1088 * Configuration information get set for mePtr; old resources 1089 * get freed, if any need it. 1090 * 1091 *---------------------------------------------------------------------- 1092 */ 1093 1094int 1095TkpConfigureMenuEntry( 1096 TkMenuEntry *mePtr) /* Information about menu entry; may 1097 * or may not already have values for 1098 * some fields. */ 1099{ 1100 TkMenu *menuPtr = mePtr->menuPtr; 1101 EntryGeometry *geometryPtr = (EntryGeometry *) mePtr->platformEntryData; 1102 1103 /* 1104 * Cascade menus have to have menu IDs of less than 256. So 1105 * we need to change the child menu if this has been configured 1106 * for a cascade item. 1107 */ 1108 1109 if (mePtr->type == CASCADE_ENTRY) { 1110 if ((mePtr->childMenuRefPtr != NULL) 1111 && (mePtr->childMenuRefPtr->menuPtr != NULL)) { 1112 MenuHandle childMenuHdl = ((MacMenu *) mePtr 1113 ->childMenuRefPtr->menuPtr->platformData)->menuHdl; 1114 1115 if (childMenuHdl != NULL) { 1116 int error = SetMenuCascade(mePtr->childMenuRefPtr->menuPtr); 1117 1118 if (error != TCL_OK) { 1119 return error; 1120 } 1121 1122 if (menuPtr->menuType == MENUBAR) { 1123 CFStringRef cfStr = CFStringCreateWithCString(NULL, 1124 (!(mePtr->labelPtr) ? "" : 1125 Tcl_GetString(mePtr->labelPtr)), 1126 kCFStringEncodingUTF8); 1127 1128 if (cfStr) { 1129 SetMenuTitleWithCFString(childMenuHdl, cfStr); 1130 CFRelease(cfStr); 1131 } 1132 } 1133 } 1134 } 1135 } 1136 1137 /* 1138 * We need to parse the accelerator string. If it has the strings 1139 * for Command, Control, Shift or Option, we need to flag it 1140 * so we can draw the symbols for it. We also need to precalcuate 1141 * the position of the first real character we are drawing. 1142 */ 1143 1144 if (0 == mePtr->accelLength) { 1145 geometryPtr->accelTextStart = -1; 1146 } else { 1147 const char *accelString = (mePtr->accelPtr == NULL) ? "" 1148 : Tcl_GetString(mePtr->accelPtr); 1149 const char *accelStart = accelString; 1150 1151 mePtr->entryFlags &= ~ENTRY_ACCEL_MASK; 1152 mePtr->entryFlags |= ParseAccelerators(&accelString, 1153 &geometryPtr->modifierNum, geometryPtr->modifierUniChars, 1154 &geometryPtr->modifierWidth); 1155 geometryPtr->accelTextStart = (ptrdiff_t)(accelString - accelStart); 1156 } 1157 1158 if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { 1159 menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 1160 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr); 1161 } 1162 1163 return TCL_OK; 1164} 1165 1166/* 1167 *---------------------------------------------------------------------- 1168 * 1169 * ReconfigureIndividualMenu -- 1170 * 1171 * This routine redoes the guts of the menu. It works from 1172 * a base item and offset, so that a regular menu will 1173 * just have all of its items added, but the help menu will 1174 * have all of its items appended after the apple-defined 1175 * items. 1176 * 1177 * Results: 1178 * None. 1179 * 1180 * Side effects: 1181 * The Macintosh menu handle is updated 1182 * 1183 *---------------------------------------------------------------------- 1184 */ 1185 1186void 1187ReconfigureIndividualMenu( 1188 TkMenu *menuPtr, /* The menu we are affecting. */ 1189 MenuHandle macMenuHdl, /* The macintosh menu we are affecting. 1190 * Will not necessarily be 1191 * menuPtr->platformData because this could 1192 * be the help menu. */ 1193 int base) /* The last index that we do not want 1194 * touched. 0 for normal menus; 1195 * # of system help menu items 1196 * for help menus. */ 1197{ 1198 int count; 1199 int index; 1200 TkMenuEntry *mePtr; 1201 int parentDisabled = 0; 1202 1203#ifdef TK_MAC_DEBUG_MENUS 1204 /* 1205 * Carbon-internal menu debugging (c.f. Technote 2124) 1206 */ 1207 1208 TkMacOSXInitNamedDebugSymbol(HIToolbox, void, DebugPrintMenu, 1209 MenuRef menu); 1210 if (DebugPrintMenu) { 1211 DebugPrintMenu(macMenuHdl); 1212 } 1213#endif 1214 1215 mePtr = GetParentMenuEntry(menuPtr); 1216 if (mePtr && mePtr->state == ENTRY_DISABLED) { 1217 parentDisabled = 1; 1218 } 1219 1220 /* 1221 * First, we get rid of all of the old items. 1222 */ 1223 1224 count = CountMenuItems(macMenuHdl); 1225 for (index = base; index < count; index++) { 1226 DeleteMenuItem(macMenuHdl, base + 1); 1227 } 1228 1229 count = menuPtr->numEntries; 1230 1231 for (index = 1; index <= count; index++) { 1232 mePtr = menuPtr->entries[index - 1]; 1233 1234 /* 1235 * We have to do separators separately because SetMenuItemText 1236 * does not parse meta-characters. 1237 */ 1238 1239 if (mePtr->type == SEPARATOR_ENTRY) { 1240 AppendMenuItemTextWithCFString(macMenuHdl, NULL, 1241 kMenuItemAttrSeparator | kMenuItemAttrDisabled, 0, NULL); 1242 } else { 1243 Tcl_DString itemTextDString; 1244 CFStringRef cfStr; 1245 1246 GetEntryText(mePtr, &itemTextDString); 1247 cfStr = CFStringCreateWithCString(NULL, 1248 Tcl_DStringValue(&itemTextDString), kCFStringEncodingUTF8); 1249 if (cfStr) { 1250 AppendMenuItemTextWithCFString(macMenuHdl, cfStr, 0, 0, NULL); 1251 CFRelease(cfStr); 1252 } else { 1253 AppendMenuItemTextWithCFString(macMenuHdl, CFSTR ("<Error>"), 1254 0, 0, NULL); 1255 } 1256 Tcl_DStringFree(&itemTextDString); 1257 1258 /* 1259 * Set enabling and disabling correctly. 1260 */ 1261 1262 if (parentDisabled || (mePtr->state == ENTRY_DISABLED)) { 1263 DisableMenuItem(macMenuHdl, base + index); 1264 } else { 1265 EnableMenuItem(macMenuHdl, base + index); 1266 } 1267 1268 /* 1269 * Set the check mark for check entries and radio entries. 1270 */ 1271 1272 SetItemMark(macMenuHdl, base + index, 0); 1273 if ((mePtr->type == CHECK_BUTTON_ENTRY) 1274 || (mePtr->type == RADIO_BUTTON_ENTRY)) { 1275 CheckMenuItem(macMenuHdl, base + index, (mePtr->entryFlags 1276 & ENTRY_SELECTED) && mePtr->indicatorOn); 1277 if (mePtr->indicatorOn 1278 && (mePtr->entryFlags & ENTRY_SELECTED)) { 1279 SetItemMark(macMenuHdl, base + index, 1280 FindMarkCharacter(mePtr)); 1281 } 1282 } 1283 1284 if (mePtr->type == CASCADE_ENTRY) { 1285 if ((mePtr->childMenuRefPtr != NULL) 1286 && (mePtr->childMenuRefPtr->menuPtr != NULL)) { 1287 MenuHandle childMenuHdl = 1288 ((MacMenu *) mePtr->childMenuRefPtr 1289 ->menuPtr->platformData)->menuHdl; 1290 1291 if (childMenuHdl != NULL) { 1292 ChkErr(SetMenuItemHierarchicalID, macMenuHdl, 1293 base + index, GetMenuID(childMenuHdl)); 1294 } 1295 /* 1296 * If we changed the highligthing of this menu, its 1297 * children all have to be reconfigured so that 1298 * their state will be reflected in the menubar. 1299 */ 1300 1301 if (!(mePtr->childMenuRefPtr->menuPtr->menuFlags 1302 & MENU_RECONFIGURE_PENDING)) { 1303 mePtr->childMenuRefPtr->menuPtr->menuFlags 1304 |= MENU_RECONFIGURE_PENDING; 1305 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, 1306 (ClientData) mePtr->childMenuRefPtr->menuPtr); 1307 } 1308 } 1309 } 1310 1311 if ((mePtr->type != CASCADE_ENTRY) && (mePtr->accelPtr != NULL)) { 1312 int accelLen, modifiers = 0, hasCmd = 0; 1313 EntryGeometry *geometryPtr = 1314 (EntryGeometry*)mePtr->platformEntryData; 1315 int offset = geometryPtr->accelTextStart; 1316 char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, &accelLen); 1317 1318 accelLen -= offset; 1319 accel += offset; 1320 if (mePtr->entryFlags & ENTRY_OPTION_ACCEL) { 1321 modifiers |= kMenuOptionModifier; 1322 } 1323 if (mePtr->entryFlags & ENTRY_SHIFT_ACCEL) { 1324 modifiers |= kMenuShiftModifier; 1325 } 1326 if (mePtr->entryFlags & ENTRY_CONTROL_ACCEL) { 1327 modifiers |= kMenuControlModifier; 1328 } 1329 if (mePtr->entryFlags & ENTRY_COMMAND_ACCEL) { 1330 hasCmd = 1; 1331 } 1332 if (accelLen == 1) { 1333 if (hasCmd || (modifiers != 0 && modifiers != 1334 kMenuShiftModifier)) { 1335 SetItemCmd(macMenuHdl, base + index, accel[0]); 1336 if (!hasCmd) { 1337 modifiers |= kMenuNoCommandModifier; 1338 } 1339 } 1340 } else { 1341 /* 1342 * Convert from accelerator names to Carbon menu glyphs. 1343 */ 1344 struct Glyph { 1345 const char *name; 1346 const size_t len; 1347 const char glyph; 1348 }; 1349#define GLYPH(n, g) { #n, sizeof(#n)-1, kMenu##g##Glyph } 1350 static const struct Glyph glyphs[] = { 1351 GLYPH(PageUp, PageUp), 1352 GLYPH(PageDown, PageDown), 1353 GLYPH(Left, LeftArrow), 1354 GLYPH(Right, RightArrow), 1355 GLYPH(Up, UpArrow), 1356 GLYPH(Down, DownArrow), 1357 GLYPH(Escape, Escape), 1358 GLYPH(Clear, Clear), 1359 GLYPH(Enter, Enter), 1360 GLYPH(Backspace,DeleteLeft), 1361 GLYPH(Space, Space), 1362 GLYPH(Tab, TabRight), 1363 GLYPH(Delete, DeleteRight), 1364 GLYPH(Home, NorthwestArrow), 1365 GLYPH(End, SoutheastArrow), 1366 GLYPH(Return, Return), 1367 GLYPH(Help, Help), 1368 GLYPH(Power, Power), 1369 { NULL, 0, 0} 1370 }; 1371#undef GLYPH 1372 const struct Glyph *g = glyphs; 1373 char glyph = 0; 1374 1375 if (accel[0] == 'F' && accelLen < 4 && 1376 (accel[1] > '0' && accel[1] <= '9')) { 1377 int fkey = accel[1] - '0'; 1378 1379 if (accelLen == 3) { 1380 if (accel[2] >= '0' && accel[2] <= '9') { 1381 fkey = 10 * fkey + (accel[2] - '0'); 1382 } else { 1383 fkey = 0; 1384 } 1385 } 1386 if (fkey >= 1 && fkey <= 12) { 1387 glyph = kMenuF1Glyph + fkey - 1; 1388 } else if (fkey >= 13 && fkey <= 15) { 1389 glyph = kMenuF13Glyph + fkey - 13; 1390 } 1391 } else while (g->name) { 1392 if (accel[0] == g->name[0] && 1393 (size_t)accelLen == g->len && 1394 !strncasecmp(accel, g->name, g->len)) { 1395 glyph = g->glyph; 1396 break; 1397 } 1398 g++; 1399 } 1400 if (glyph) { 1401 ChkErr(SetMenuItemKeyGlyph, macMenuHdl, base + index, 1402 glyph); 1403 if (!hasCmd) { 1404 modifiers |= kMenuNoCommandModifier; 1405 } 1406 geometryPtr->accelGlyph = glyph; 1407 } 1408 } 1409 ChkErr(SetMenuItemModifiers, macMenuHdl, base + index, 1410 modifiers); 1411 } 1412 } 1413 } 1414} 1415 1416/* 1417 *---------------------------------------------------------------------- 1418 * 1419 * ReconfigureMacintoshMenu -- 1420 * 1421 * Rebuilds the Macintosh MenuHandle items from the menu. Called 1422 * usually as an idle handler, but can be called synchronously 1423 * if the menu is about to be posted. 1424 * 1425 * Results: 1426 * None. 1427 * 1428 * Side effects: 1429 * Configuration information get set for mePtr; old resources 1430 * get freed, if any need it. 1431 * 1432 *---------------------------------------------------------------------- 1433 */ 1434 1435void 1436ReconfigureMacintoshMenu( 1437 ClientData clientData) /* Information about menu entry; may 1438 * or may not already have values for 1439 * some fields. */ 1440{ 1441 TkMenu *menuPtr = (TkMenu *) clientData; 1442 MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; 1443 MenuHandle helpMenuHdl = NULL; 1444 1445 menuPtr->menuFlags &= ~MENU_RECONFIGURE_PENDING; 1446 1447 if (NULL == macMenuHdl) { 1448 return; 1449 } 1450 1451 ReconfigureIndividualMenu(menuPtr, macMenuHdl, 0); 1452 1453 if (GetMenuID(macMenuHdl) == currentHelpMenuID) { 1454 MenuItemIndex helpIndex; 1455 HMGetHelpMenu(&helpMenuHdl,&helpIndex); 1456 if (helpMenuHdl != NULL) { 1457 ReconfigureIndividualMenu(menuPtr, helpMenuHdl, helpIndex - 1); 1458 } 1459 } 1460 1461 if (menuPtr->menuType == MENUBAR) { 1462 if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { 1463 Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); 1464 menuBarFlags |= MENUBAR_REDRAW_PENDING; 1465 } 1466 } 1467} 1468 1469/* 1470 *---------------------------------------------------------------------- 1471 * 1472 * CompleteIdlers -- 1473 * 1474 * Completes all idle handling so that the menus are in sync when 1475 * the user invokes them with the mouse. 1476 * 1477 * Results: 1478 * None. 1479 * 1480 * Side effects: 1481 * The Macintosh menu handles are flushed out. 1482 * 1483 *---------------------------------------------------------------------- 1484 */ 1485 1486void 1487CompleteIdlers( 1488 TkMenu *menuPtr) /* The menu we are completing. */ 1489{ 1490 int i; 1491 1492 if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) { 1493 Tcl_CancelIdleCall(ReconfigureMacintoshMenu, (ClientData) menuPtr); 1494 ReconfigureMacintoshMenu((ClientData) menuPtr); 1495 } 1496 1497 for (i = 0; i < menuPtr->numEntries; i++) { 1498 if ((menuPtr->entries[i]->type == CASCADE_ENTRY) && 1499 (menuPtr->entries[i]->childMenuRefPtr != NULL) && 1500 (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) { 1501 CompleteIdlers(menuPtr->entries[i]->childMenuRefPtr->menuPtr); 1502 } 1503 } 1504} 1505 1506/* 1507 *---------------------------------------------------------------------- 1508 * 1509 * TkpPostMenu -- 1510 * 1511 * Posts a menu on the screen 1512 * 1513 * Results: 1514 * None. 1515 * 1516 * Side effects: 1517 * The menu is posted and handled. 1518 * 1519 *---------------------------------------------------------------------- 1520 */ 1521 1522int 1523TkpPostMenu( 1524 Tcl_Interp *interp, /* The interpreter this menu lives in */ 1525 TkMenu *menuPtr, /* The menu we are posting */ 1526 int x, /* The global x-coordinate of the top, left- 1527 * hand corner of where the menu is supposed 1528 * to be posted. */ 1529 int y) /* The global y-coordinate */ 1530{ 1531 MenuHandle macMenuHdl = ((MacMenu *) menuPtr->platformData)->menuHdl; 1532 long popUpResult; 1533 int result; 1534 1535 if (inPostMenu > 0) { 1536 Tcl_AppendResult(interp, 1537 "Cannot call post menu while already posting menu", NULL); 1538 result = TCL_ERROR; 1539 } else { 1540 short menuID; 1541 Window window; 1542 int oldWidth = menuPtr->totalWidth; 1543 1544 inPostMenu++; 1545 result = TkPreprocessMenu(menuPtr); 1546 /* 1547 * The post commands could have deleted the menu, which means 1548 * we are dead and should go away. 1549 */ 1550 1551 if (result != TCL_OK || !menuPtr->tkwin) { 1552 goto endPostMenu; 1553 } 1554 1555 CompleteIdlers(menuPtr); 1556 if (menuBarFlags & MENUBAR_REDRAW_PENDING) { 1557 Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL); 1558 DrawMenuBarWhenIdle(NULL); 1559 } 1560 RecursivelyInsertMenu(menuPtr); 1561 1562 TkMacOSXTrackingLoop(1); 1563 popUpResult = PopUpMenuSelect(macMenuHdl, y, x, menuPtr->active); 1564 TkMacOSXTrackingLoop(0); 1565 menuPtr->totalWidth = oldWidth; 1566 1567 /* 1568 * Simulate the mouse up. 1569 */ 1570 1571 window = Tk_WindowId(menuPtr->tkwin); 1572 TkGenerateButtonEventForXPointer(window); 1573 1574 /* 1575 * Dispatch the command. 1576 */ 1577 1578 menuID = HiWord(popUpResult); 1579 if (menuID != 0) { 1580 result = TkMacOSXDispatchMenuEvent(menuID, LoWord(popUpResult)); 1581 } 1582 1583endPostMenu: 1584 inPostMenu--; 1585 } 1586 return result; 1587} 1588 1589/* 1590 *---------------------------------------------------------------------- 1591 * 1592 * TkpMenuNewEntry -- 1593 * 1594 * Adds a pointer to a new menu entry structure with the platform- 1595 * specific fields filled in. The Macintosh uses the 1596 * platformEntryData field of the TkMenuEntry record to store 1597 * geometry information. 1598 * 1599 * Results: 1600 * Standard TCL error. 1601 * 1602 * Side effects: 1603 * Storage gets allocated. New menu entry data is put into the 1604 * platformEntryData field of the mePtr. 1605 * 1606 *---------------------------------------------------------------------- 1607 */ 1608 1609int 1610TkpMenuNewEntry( 1611 TkMenuEntry *mePtr) /* The menu we are adding an entry to */ 1612{ 1613 EntryGeometry *geometryPtr = 1614 (EntryGeometry *) ckalloc(sizeof(EntryGeometry)); 1615 TkMenu *menuPtr = mePtr->menuPtr; 1616 1617 geometryPtr->accelTextStart = 0; 1618 geometryPtr->accelTextWidth = 0; 1619 geometryPtr->nonAccelMargin = 0; 1620 geometryPtr->modifierWidth = 0; 1621 geometryPtr->modifierNum = 0; 1622 geometryPtr->accelGlyph = 0; 1623 mePtr->platformEntryData = (TkMenuPlatformEntryData) geometryPtr; 1624 if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { 1625 menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 1626 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) menuPtr); 1627 } 1628 return TCL_OK; 1629} 1630 1631/* 1632 *---------------------------------------------------------------------- 1633 * 1634 * Tk_MacOSXTurnOffMenus -- 1635 * 1636 * Turns off all the menu drawing code. This is more than just disabling 1637 * the "menu" command, this means that Tk will NEVER touch the menubar. 1638 * It is needed in the Plugin, where Tk does not own the menubar. 1639 * 1640 * Results: 1641 * None. 1642 * 1643 * Side effects: 1644 * A flag is set which will disable all menu drawing. 1645 * 1646 *---------------------------------------------------------------------- 1647 */ 1648 1649void 1650Tk_MacOSXTurnOffMenus(void) 1651{ 1652 gNoTkMenus = 1; 1653} 1654 1655/* 1656 *---------------------------------------------------------------------- 1657 * 1658 * DrawMenuBarWhenIdle -- 1659 * 1660 * Update the menu bar next time there is an idle event. 1661 * 1662 * Results: 1663 * None. 1664 * 1665 * Side effects: 1666 * Menu bar is redrawn. 1667 * 1668 *---------------------------------------------------------------------- 1669 */ 1670 1671void 1672DrawMenuBarWhenIdle( 1673 ClientData clientData) /* ignored here */ 1674{ 1675 TkMenuReferences *menuRefPtr; 1676 TkMenu *appleMenuPtr, *helpMenuPtr, *menuBarPtr = NULL; 1677 MenuHandle macMenuHdl; 1678 Tcl_HashEntry *hashEntryPtr; 1679 1680 /* 1681 * If we have been turned off, exit. 1682 */ 1683 1684 if (gNoTkMenus) { 1685 return; 1686 } 1687 1688 /* 1689 * We need to clear the apple and help menus of any extra items. 1690 */ 1691 1692 if (currentAppleMenuID != 0) { 1693 hashEntryPtr = Tcl_FindHashEntry(&commandTable, 1694 (char*)(intptr_t)currentAppleMenuID); 1695 appleMenuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr); 1696 TkpDestroyMenu(appleMenuPtr); 1697 TkpNewMenu(appleMenuPtr); 1698 appleMenuPtr->menuFlags &= ~MENU_APPLE_MENU; 1699 appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 1700 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, (ClientData) appleMenuPtr); 1701 } 1702 1703 if (currentHelpMenuID != 0) { 1704 hashEntryPtr = Tcl_FindHashEntry(&commandTable, 1705 (char*)(intptr_t)currentHelpMenuID); 1706 helpMenuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr); 1707 TkpDestroyMenu(helpMenuPtr); 1708 TkpNewMenu(helpMenuPtr); 1709 helpMenuPtr->menuFlags &= ~MENU_HELP_MENU; 1710 helpMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 1711 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, 1712 (ClientData) helpMenuPtr); 1713 } 1714 1715 /* 1716 * We need to find the clone of this menu that is the menubar. 1717 * Once we do that, for every cascade in the menu, we need to 1718 * insert the Mac menu in the Mac menubar. Finally, we need 1719 * to redraw the menubar. 1720 */ 1721 1722 menuRefPtr = NULL; 1723 if (currentMenuBarName != NULL) { 1724 menuRefPtr = TkFindMenuReferences(currentMenuBarInterp, 1725 currentMenuBarName); 1726 } 1727 if (menuRefPtr) { 1728 TkMenu *menuPtr; 1729 TkMenu *cascadeMenuPtr; 1730 char *appleMenuName, *helpMenuName; 1731 int appleIndex = -1, helpIndex = -1, i; 1732 1733 menuPtr = menuRefPtr->menuPtr; 1734 if (menuPtr != NULL) { 1735 TkMenuReferences *specialMenuRefPtr; 1736 TkMenuEntry *specialEntryPtr; 1737 1738 appleMenuName = ckalloc(strlen(currentMenuBarName) + 1 + 1739 strlen(".apple") + 1); 1740 sprintf(appleMenuName, "%s.apple", Tk_PathName(menuPtr->tkwin)); 1741 specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp, 1742 appleMenuName); 1743 if ((specialMenuRefPtr != NULL) 1744 && (specialMenuRefPtr->menuPtr != NULL)) { 1745 for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr; 1746 specialEntryPtr != NULL; 1747 specialEntryPtr = specialEntryPtr->nextCascadePtr) { 1748 if (specialEntryPtr->menuPtr == menuPtr) { 1749 appleIndex = specialEntryPtr->index; 1750 break; 1751 } 1752 } 1753 } 1754 ckfree(appleMenuName); 1755 1756 helpMenuName = ckalloc(strlen(currentMenuBarName) + 1 + 1757 strlen(".help") + 1); 1758 sprintf(helpMenuName, "%s.help", Tk_PathName(menuPtr->tkwin)); 1759 specialMenuRefPtr = TkFindMenuReferences(currentMenuBarInterp, 1760 helpMenuName); 1761 if ((specialMenuRefPtr != NULL) 1762 && (specialMenuRefPtr->menuPtr != NULL)) { 1763 for (specialEntryPtr = specialMenuRefPtr->parentEntryPtr; 1764 specialEntryPtr != NULL; 1765 specialEntryPtr = specialEntryPtr->nextCascadePtr) { 1766 if (specialEntryPtr->menuPtr == menuPtr) { 1767 helpIndex = specialEntryPtr->index; 1768 break; 1769 } 1770 } 1771 } 1772 ckfree(helpMenuName); 1773 } 1774 1775 for (menuBarPtr = menuPtr; 1776 (menuBarPtr != NULL) && (menuBarPtr->menuType != MENUBAR); 1777 menuBarPtr = menuBarPtr->nextInstancePtr) { 1778 /* 1779 * Null loop body. 1780 */ 1781 } 1782 1783 if (menuBarPtr) { 1784 if (menuBarPtr->tearoff != menuPtr->tearoff) { 1785 if (menuBarPtr->tearoff) { 1786 appleIndex = (-1 == appleIndex) ? appleIndex 1787 : appleIndex + 1; 1788 helpIndex = (-1 == helpIndex) ? helpIndex 1789 : helpIndex + 1; 1790 } else { 1791 appleIndex = (-1 == appleIndex) ? appleIndex 1792 : appleIndex - 1; 1793 helpIndex = (-1 == helpIndex) ? helpIndex 1794 : helpIndex - 1; 1795 } 1796 } 1797 ClearMenuBar(); 1798 1799 if (appleIndex == -1) { 1800 InsertMenu(tkAppleMenu, 0); 1801 currentAppleMenuID = 0; 1802 tkCurrentAppleMenu = tkAppleMenu; 1803 } else { 1804 short appleID; 1805 1806 appleMenuPtr = menuBarPtr->entries[appleIndex] 1807 ->childMenuRefPtr->menuPtr; 1808 TkpDestroyMenu(appleMenuPtr); 1809 TkMacOSXGetNewMenuID(appleMenuPtr->interp, appleMenuPtr, 0, 1810 &appleID); 1811 macMenuHdl = NewMenu(appleID, "\p\024"); 1812 appleMenuPtr->platformData = 1813 (TkMenuPlatformData) ckalloc(sizeof(MacMenu)); 1814 ((MacMenu *)appleMenuPtr->platformData)->menuHdl 1815 = macMenuHdl; 1816 appleMenuPtr->menuFlags |= MENU_APPLE_MENU; 1817 if (!(appleMenuPtr->menuFlags 1818 & MENU_RECONFIGURE_PENDING)) { 1819 appleMenuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; 1820 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, 1821 (ClientData) appleMenuPtr); 1822 } 1823 InsertMenu(macMenuHdl, 0); 1824 RecursivelyInsertMenu(appleMenuPtr); 1825 currentAppleMenuID = appleID; 1826 tkCurrentAppleMenu = macMenuHdl; 1827 } 1828 if (helpIndex == -1) { 1829 currentHelpMenuID = 0; 1830 } 1831 1832 for (i = 0; i < menuBarPtr->numEntries; i++) { 1833 if (i == appleIndex) { 1834 if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) { 1835 DisableMenuItem(((MacMenu *) menuBarPtr->entries[i] 1836 ->childMenuRefPtr->menuPtr 1837 ->platformData)->menuHdl, 0); 1838 } else { 1839 EnableMenuItem(((MacMenu *) menuBarPtr->entries[i] 1840 ->childMenuRefPtr->menuPtr 1841 ->platformData)->menuHdl, 0); 1842 } 1843 continue; 1844 } else if (i == helpIndex) { 1845 TkMenu *helpMenuPtr = menuBarPtr->entries[i] 1846 ->childMenuRefPtr->menuPtr; 1847 1848 if (helpMenuPtr == NULL) { 1849 continue; 1850 } 1851 helpMenuPtr->menuFlags |= MENU_HELP_MENU; 1852 if (!(helpMenuPtr->menuFlags 1853 & MENU_RECONFIGURE_PENDING)) { 1854 helpMenuPtr->menuFlags 1855 |= MENU_RECONFIGURE_PENDING; 1856 Tcl_DoWhenIdle(ReconfigureMacintoshMenu, 1857 (ClientData) helpMenuPtr); 1858 } 1859 macMenuHdl = 1860 ((MacMenu *) helpMenuPtr->platformData)->menuHdl; 1861 currentHelpMenuID = GetMenuID(macMenuHdl); 1862 } else if (menuBarPtr->entries[i]->type 1863 == CASCADE_ENTRY) { 1864 if ((menuBarPtr->entries[i]->childMenuRefPtr != NULL) 1865 && menuBarPtr->entries[i]->childMenuRefPtr 1866 ->menuPtr != NULL) { 1867 cascadeMenuPtr = menuBarPtr->entries[i] 1868 ->childMenuRefPtr->menuPtr; 1869 macMenuHdl = ((MacMenu *) cascadeMenuPtr 1870 ->platformData)->menuHdl; 1871 DeleteMenu(GetMenuID(macMenuHdl)); 1872 InsertMenu(macMenuHdl, 0); 1873 RecursivelyInsertMenu(cascadeMenuPtr); 1874 if (menuBarPtr->entries[i]->state == ENTRY_DISABLED) { 1875 DisableMenuItem(((MacMenu *) menuBarPtr->entries[i] 1876 ->childMenuRefPtr->menuPtr 1877 ->platformData)->menuHdl, 0); 1878 } else { 1879 EnableMenuItem(((MacMenu *) menuBarPtr->entries[i] 1880 ->childMenuRefPtr->menuPtr 1881 ->platformData)->menuHdl, 0); 1882 } 1883 } 1884 } 1885 } 1886 } 1887 } 1888 if (!menuRefPtr || !menuBarPtr) { 1889 SetDefaultMenubar(); 1890 } 1891 DrawMenuBar(); 1892 menuBarFlags &= ~MENUBAR_REDRAW_PENDING; 1893} 1894 1895/* 1896 *---------------------------------------------------------------------- 1897 * 1898 * RecursivelyInsertMenu -- 1899 * 1900 * Puts all of the cascades of this menu in the Mac hierarchical list. 1901 * 1902 * Results: 1903 * None. 1904 * 1905 * Side effects: 1906 * The menubar is changed. 1907 * 1908 *---------------------------------------------------------------------- 1909 */ 1910 1911void 1912RecursivelyInsertMenu( 1913 TkMenu *menuPtr) /* All of the cascade items in this menu 1914 * will be inserted into the mac menubar. */ 1915{ 1916 int i; 1917 TkMenu *cascadeMenuPtr; 1918 MenuHandle macMenuHdl; 1919 1920 for (i = 0; i < menuPtr->numEntries; i++) { 1921 if (menuPtr->entries[i]->type == CASCADE_ENTRY) { 1922 if ((menuPtr->entries[i]->childMenuRefPtr != NULL) && 1923 (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) { 1924 cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr; 1925 macMenuHdl = 1926 ((MacMenu *) cascadeMenuPtr->platformData)->menuHdl; 1927 InsertMenu(macMenuHdl, -1); 1928 RecursivelyInsertMenu(cascadeMenuPtr); 1929 } 1930 } 1931 } 1932} 1933 1934/* 1935 *---------------------------------------------------------------------- 1936 * 1937 * RecursivelyDeleteMenu -- 1938 * 1939 * Takes all of the cascades of this menu out of the Mac hierarchical 1940 * list. 1941 * 1942 * Results: 1943 * None. 1944 * 1945 * Side effects: 1946 * The menubar is changed. 1947 * 1948 *---------------------------------------------------------------------- 1949 */ 1950 1951void 1952RecursivelyDeleteMenu( 1953 TkMenu *menuPtr) /* All of the cascade items in this menu 1954 * will be deleted from the mac menubar. */ 1955{ 1956 int i; 1957 TkMenu *cascadeMenuPtr; 1958 MenuHandle macMenuHdl; 1959 1960 for (i = 0; i < menuPtr->numEntries; i++) { 1961 if (menuPtr->entries[i]->type == CASCADE_ENTRY) { 1962 if ((menuPtr->entries[i]->childMenuRefPtr != NULL) && 1963 (menuPtr->entries[i]->childMenuRefPtr->menuPtr != NULL)) { 1964 cascadeMenuPtr = menuPtr->entries[i]->childMenuRefPtr->menuPtr; 1965 macMenuHdl = 1966 ((MacMenu *) cascadeMenuPtr->platformData)->menuHdl; 1967 DeleteMenu(GetMenuID(macMenuHdl)); 1968 RecursivelyDeleteMenu(cascadeMenuPtr); 1969 } 1970 } 1971 } 1972} 1973 1974/* 1975 *---------------------------------------------------------------------- 1976 * 1977 * SetDefaultMenubar -- 1978 * 1979 * Puts the Apple, File and Edit menus into the Macintosh menubar. 1980 * 1981 * Results: 1982 * None. 1983 * 1984 * Side effects: 1985 * The menubar is changed. 1986 * 1987 *---------------------------------------------------------------------- 1988 */ 1989 1990void 1991SetDefaultMenubar(void) 1992{ 1993 if (currentMenuBarName != NULL) { 1994 ckfree(currentMenuBarName); 1995 currentMenuBarName = NULL; 1996 } 1997 currentMenuBarOwner = NULL; 1998 ClearMenuBar(); 1999 InsertMenu(tkAppleMenu, 0); 2000 InsertMenu(tkFileMenu, 0); 2001 InsertMenu(tkEditMenu, 0); 2002 if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { 2003 Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); 2004 menuBarFlags |= MENUBAR_REDRAW_PENDING; 2005 } 2006} 2007 2008/* 2009 *---------------------------------------------------------------------- 2010 * 2011 * TkpSetMainMenubar -- 2012 * 2013 * Puts the menu associated with a window into the menubar. Should 2014 * only be called when the window is in front. 2015 * 2016 * Results: 2017 * None. 2018 * 2019 * Side effects: 2020 * The menubar is changed. 2021 * 2022 *---------------------------------------------------------------------- 2023 */ 2024 2025void 2026TkpSetMainMenubar( 2027 Tcl_Interp *interp, /* The interpreter of the application */ 2028 Tk_Window tkwin, /* The frame we are setting up */ 2029 char *menuName) /* The name of the menu to put in front. 2030 * If NULL, use the default menu bar. 2031 */ 2032{ 2033 TkWindow *winPtr = (TkWindow *) tkwin; 2034 WindowRef macWindowPtr; 2035 WindowRef frontNonFloating; 2036 2037 macWindowPtr = TkMacOSXDrawableWindow(winPtr->window); 2038 2039 frontNonFloating = ActiveNonFloatingWindow(); 2040 if ((macWindowPtr == NULL) || (macWindowPtr != frontNonFloating)) { 2041 return; 2042 } 2043 2044 if ((currentMenuBarInterp != interp) || (currentMenuBarOwner != tkwin) 2045 || (currentMenuBarName == NULL) || (menuName == NULL) 2046 || (strcmp(menuName, currentMenuBarName) != 0)) { 2047 Tk_Window searchWindow; 2048 TopLevelMenubarList *listPtr; 2049 2050 if (currentMenuBarName != NULL) { 2051 ckfree(currentMenuBarName); 2052 } 2053 2054 if (menuName == NULL) { 2055 searchWindow = tkwin; 2056 if (strcmp(Tk_Class(searchWindow), "Menu") == 0) { 2057 TkMenuReferences *menuRefPtr; 2058 2059 menuRefPtr = TkFindMenuReferences(interp, Tk_PathName(tkwin)); 2060 if (menuRefPtr != NULL) { 2061 TkMenu *menuPtr = menuRefPtr->menuPtr; 2062 2063 if (menuPtr != NULL) { 2064 searchWindow = menuPtr->masterMenuPtr->tkwin; 2065 } 2066 } 2067 } 2068 for (; searchWindow != NULL; 2069 searchWindow = Tk_Parent(searchWindow)) { 2070 for (listPtr = windowListPtr; listPtr != NULL; 2071 listPtr = listPtr->nextPtr) { 2072 if (listPtr->tkwin == searchWindow) { 2073 break; 2074 } 2075 } 2076 if (listPtr != NULL) { 2077 menuName = Tk_PathName( 2078 listPtr->menuPtr->masterMenuPtr->tkwin); 2079 break; 2080 } 2081 } 2082 } 2083 2084 if (menuName == NULL) { 2085 currentMenuBarName = NULL; 2086 } else { 2087 currentMenuBarName = ckalloc(strlen(menuName) + 1); 2088 strcpy(currentMenuBarName, menuName); 2089 } 2090 currentMenuBarOwner = tkwin; 2091 currentMenuBarInterp = interp; 2092 } 2093 if (!(menuBarFlags & MENUBAR_REDRAW_PENDING)) { 2094 Tcl_DoWhenIdle(DrawMenuBarWhenIdle, NULL); 2095 menuBarFlags |= MENUBAR_REDRAW_PENDING; 2096 } 2097} 2098 2099/* 2100 *---------------------------------------------------------------------- 2101 * 2102 * TkpSetWindowMenuBar -- 2103 * 2104 * Associates a given menu with a window. 2105 * 2106 * Results: 2107 * None. 2108 * 2109 * Side effects: 2110 * On Windows and UNIX, associates the platform menu with the 2111 * platform window. 2112 * 2113 *---------------------------------------------------------------------- 2114 */ 2115 2116void 2117TkpSetWindowMenuBar( 2118 Tk_Window tkwin, /* The window we are setting the menu in */ 2119 TkMenu *menuPtr) /* The menu we are setting */ 2120{ 2121 TopLevelMenubarList *listPtr, *prevPtr; 2122 2123 /* 2124 * Remove any existing reference to this window. 2125 */ 2126 2127 for (prevPtr = NULL, listPtr = windowListPtr; 2128 listPtr != NULL; 2129 prevPtr = listPtr, listPtr = listPtr->nextPtr) { 2130 if (listPtr->tkwin == tkwin) { 2131 break; 2132 } 2133 } 2134 2135 if (listPtr != NULL) { 2136 if (prevPtr != NULL) { 2137 prevPtr->nextPtr = listPtr->nextPtr; 2138 } else { 2139 windowListPtr = listPtr->nextPtr; 2140 } 2141 ckfree((char *) listPtr); 2142 } 2143 2144 if (menuPtr != NULL) { 2145 listPtr = (TopLevelMenubarList *) ckalloc(sizeof(TopLevelMenubarList)); 2146 listPtr->nextPtr = windowListPtr; 2147 windowListPtr = listPtr; 2148 listPtr->tkwin = tkwin; 2149 listPtr->menuPtr = menuPtr; 2150 } 2151} 2152 2153/* 2154 *---------------------------------------------------------------------- 2155 * 2156 * EventuallyInvokeMenu -- 2157 * 2158 * This IdleTime callback actually invokes the menu command 2159 * scheduled in TkMacOSXDispatchMenuEvent. 2160 * 2161 * Results: 2162 * None. 2163 * 2164 * Side effects: 2165 * Commands get executed. 2166 * 2167 *---------------------------------------------------------------------- 2168 */ 2169 2170void 2171EventuallyInvokeMenu ( 2172 ClientData data) 2173{ 2174 struct MenuCommandHandlerData *realData = 2175 (struct MenuCommandHandlerData *) data; 2176 int code; 2177 2178 code = TkInvokeMenu(realData->menuPtr->interp, realData->menuPtr, 2179 realData->index); 2180 2181 if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) { 2182 Tcl_AddErrorInfo(realData->menuPtr->interp, "\n (menu invoke)"); 2183 Tcl_BackgroundError(realData->menuPtr->interp); 2184 } 2185 2186 if (realData->menuPtr->tkwin) { 2187 RecursivelyClearActiveMenu(realData->menuPtr); 2188 } 2189 TkMacOSXClearMenubarActive(); 2190 2191 Tcl_Release(realData->menuPtr->interp); 2192 Tcl_Release(realData->menuPtr); 2193} 2194 2195/* 2196 *---------------------------------------------------------------------- 2197 * 2198 * TkMacOSXDispatchMenuEvent -- 2199 * 2200 * Given a menu id and an item, dispatches the command associated 2201 * with it. 2202 * 2203 * Results: 2204 * None. 2205 * 2206 * Side effects: 2207 * Commands for the event are scheduled for execution at idle time. 2208 * 2209 *---------------------------------------------------------------------- 2210 */ 2211 2212int 2213TkMacOSXDispatchMenuEvent( 2214 int menuID, /* The menu id of the menu we are invoking */ 2215 int index) /* The one-based index of the item that was 2216 * selected. */ 2217{ 2218 int result = TCL_OK; 2219 2220 if (menuID != 0) { 2221 if (menuID == kHMHelpMenuID) { 2222 if (currentMenuBarOwner != NULL) { 2223 TkMenuReferences *helpMenuRef; 2224 char *helpMenuName = ckalloc(strlen(currentMenuBarName) 2225 + strlen(".help") + 1); 2226 2227 sprintf(helpMenuName, "%s.help", currentMenuBarName); 2228 helpMenuRef = TkFindMenuReferences(currentMenuBarInterp, 2229 helpMenuName); 2230 ckfree(helpMenuName); 2231 if ((helpMenuRef != NULL) && (helpMenuRef->menuPtr != NULL)) { 2232 MenuRef outHelpMenu; 2233 MenuItemIndex itemIndex; 2234 int newIndex; 2235 2236 HMGetHelpMenu(&outHelpMenu, &itemIndex); 2237 newIndex = index - itemIndex; 2238 result = TkInvokeMenu(currentMenuBarInterp, 2239 helpMenuRef->menuPtr, newIndex); 2240 } 2241 } 2242 } else { 2243 Tcl_HashEntry *commandEntryPtr = 2244 Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID); 2245 if (commandEntryPtr != NULL) { 2246 TkMenu *menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr); 2247 2248 if ((currentAppleMenuID == menuID) 2249 && (index > menuPtr->numEntries + 1)) { 2250 /* 2251 * We don't need to do anything here, the standard 2252 * Application event handler will open the built-in 2253 * Apple menu item for us. 2254 */ 2255 result = TCL_OK; 2256 } else { 2257 struct MenuCommandHandlerData *data 2258 = (struct MenuCommandHandlerData *) 2259 ckalloc(sizeof(struct MenuCommandHandlerData)); 2260 2261 Tcl_Preserve(menuPtr->interp); 2262 Tcl_Preserve(menuPtr); 2263 data->menuPtr = menuPtr; 2264 data->index = index - 1; 2265 Tcl_DoWhenIdle(EventuallyInvokeMenu, 2266 (ClientData) data); 2267 /* result = TkInvokeMenu(menuPtr->interp, menuPtr, index - 1); */ 2268 } 2269 } else { 2270 return TCL_ERROR; 2271 } 2272 } 2273 } 2274 return result; 2275} 2276 2277/* 2278 *---------------------------------------------------------------------- 2279 * 2280 * GetMenuIndicatorGeometry -- 2281 * 2282 * Gets the width and height of the indicator area of a menu. 2283 * 2284 * Results: 2285 * widthPtr and heightPtr are set. 2286 * 2287 * Side effects: 2288 * None. 2289 * 2290 *---------------------------------------------------------------------- 2291 */ 2292 2293void 2294GetMenuIndicatorGeometry ( 2295 TkMenu *menuPtr, /* The menu we are drawing */ 2296 TkMenuEntry *mePtr, /* The entry we are measuring */ 2297 Tk_Font tkfont, /* Precalculated font */ 2298 const Tk_FontMetrics *fmPtr,/* Precalculated font metrics */ 2299 int *widthPtr, /* The resulting width */ 2300 int *heightPtr) /* The resulting height */ 2301{ 2302 *heightPtr = fmPtr->linespace + menuItemExtraHeight; 2303 if (IS_THEME_MENU_FONT(tkfont)) { 2304 *widthPtr = menuMarkColumnWidth; 2305 } else { 2306 const char markChar = FindMarkCharacter(mePtr); 2307 const char *markUtf = NULL; 2308 int len; 2309 2310 len = GetUtfMarkCharacter(markChar, &markUtf); 2311 *widthPtr = Tk_TextWidth(tkfont, markUtf, len) + 2*menuMarkIndent; 2312 } 2313} 2314 2315/* 2316 *---------------------------------------------------------------------- 2317 * 2318 * GetMenuAccelGeometry -- 2319 * 2320 * Gets the width and height of the accelerator area of a menu. 2321 * 2322 * Results: 2323 * widthPtr and heightPtr are set. 2324 * 2325 * Side effects: 2326 * None. 2327 * 2328 *---------------------------------------------------------------------- 2329 */ 2330 2331void 2332GetMenuAccelGeometry ( 2333 TkMenu *menuPtr, /* The menu we are measuring */ 2334 TkMenuEntry *mePtr, /* The entry we are measuring */ 2335 Tk_Font tkfont, /* The precalculated font */ 2336 const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 2337 int *modWidthPtr, /* The width of all of the key 2338 * modifier symbols. */ 2339 int *textWidthPtr, /* The resulting width */ 2340 int *heightPtr) /* The resulting height */ 2341{ 2342 *heightPtr = fmPtr->linespace + menuItemExtraHeight; 2343 *modWidthPtr = menuSymbols[COMMAND_SYMBOL].width; 2344 *textWidthPtr = 0; 2345 if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) { 2346 const char *accel = (mePtr->accelPtr == NULL) ? "" 2347 : Tcl_GetString(mePtr->accelPtr); 2348 EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData; 2349 2350 if (IS_THEME_MENU_FONT(tkfont)) { 2351 CFStringRef cfStr; 2352 int width = 0; 2353 int maxWidth = ((TkFont *)tkfont)->fm.maxWidth; 2354 2355 if (geometryPtr->accelGlyph) { 2356 cfStr = CFStringCreateWithBytes(NULL, 2357 (UInt8*)&geometryPtr->accelGlyph, 1, 2358 kTextEncodingMacKeyboardGlyphs, false); 2359 if (cfStr) { 2360 width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont); 2361 CFRelease(cfStr); 2362 } 2363 } 2364 if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) { 2365 if (!geometryPtr->accelGlyph) { 2366 width = Tk_TextWidth(tkfont, accel, mePtr->accelLength); 2367 } 2368 *textWidthPtr = maxWidth; 2369 if (width < maxWidth) { 2370 *modWidthPtr = 0; 2371 } else { 2372 *modWidthPtr = width - maxWidth; 2373 } 2374 } else { 2375 if (!geometryPtr->accelGlyph) { 2376 width = Tk_TextWidth(tkfont, accel + 2377 geometryPtr->accelTextStart, mePtr->accelLength - 2378 geometryPtr->accelTextStart); 2379 } 2380 if (width < maxWidth) { 2381 *textWidthPtr = maxWidth; 2382 } else { 2383 *textWidthPtr = width; 2384 } 2385 if (geometryPtr->modifierNum) { 2386 *modWidthPtr = geometryPtr->modifierWidth; 2387 } 2388 } 2389 } else { 2390 *textWidthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength); 2391 } 2392 } 2393} 2394 2395/* 2396 *---------------------------------------------------------------------- 2397 * 2398 * GetTearoffEntryGeometry -- 2399 * 2400 * Gets the width and height of of a tearoff entry. 2401 * 2402 * Results: 2403 * widthPtr and heightPtr are set. 2404 * 2405 * Side effects: 2406 * None. 2407 * 2408 *---------------------------------------------------------------------- 2409 */ 2410 2411void 2412GetTearoffEntryGeometry ( 2413 TkMenu *menuPtr, /* The menu we are drawing */ 2414 TkMenuEntry *mePtr, /* The entry we are measuring */ 2415 Tk_Font tkfont, /* The precalculated font */ 2416 const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 2417 int *widthPtr, /* The resulting width */ 2418 int *heightPtr) /* The resulting height */ 2419{ 2420#ifdef USE_TK_MDEF 2421 const int useMDEF = ((MacMenu *) menuPtr->platformData)->useMDEF; 2422#endif 2423 if (useMDEF && menuPtr->menuType != TEAROFF_MENU) { 2424 *heightPtr = fmPtr->linespace + menuItemExtraHeight; 2425 *widthPtr = menuPtr->totalWidth; 2426 } else { 2427 *widthPtr = *heightPtr = 0; 2428 } 2429} 2430 2431/* 2432 *---------------------------------------------------------------------- 2433 * 2434 * GetMenuSeparatorGeometry -- 2435 * 2436 * Gets the width and height of menu separator. 2437 * 2438 * Results: 2439 * widthPtr and heightPtr are set. 2440 * 2441 * Side effects: 2442 * None. 2443 * 2444 *---------------------------------------------------------------------- 2445 */ 2446 2447void 2448GetMenuSeparatorGeometry( 2449 TkMenu *menuPtr, /* The menu we are drawing */ 2450 TkMenuEntry *mePtr, /* The entry we are measuring */ 2451 Tk_Font tkfont, /* The precalculated font */ 2452 const Tk_FontMetrics *fmPtr,/* The precalcualted font metrics */ 2453 int *widthPtr, /* The resulting width */ 2454 int *heightPtr) /* The resulting height */ 2455{ 2456 *widthPtr = 0; 2457 *heightPtr = menuSeparatorHeight; 2458} 2459 2460/* 2461 *---------------------------------------------------------------------- 2462 * 2463 * DrawMenuEntryIndicator -- 2464 * 2465 * This procedure draws the indicator part of a menu. 2466 * 2467 * Results: 2468 * None. 2469 * 2470 * Side effects: 2471 * Commands are output to X to display the menu in its 2472 * current mode. 2473 * 2474 *---------------------------------------------------------------------- 2475 */ 2476 2477void 2478DrawMenuEntryIndicator( 2479 TkMenu *menuPtr, /* The menu we are drawing */ 2480 TkMenuEntry *mePtr, /* The entry we are drawing */ 2481 Drawable d, /* The drawable we are drawing */ 2482 GC gc, /* The GC we are drawing with */ 2483 GC indicatorGC, /* The GC to use for the indicator */ 2484 Tk_Font tkfont, /* The precalculated font */ 2485 const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 2486 int x, /* topleft hand corner of entry */ 2487 int y, /* topleft hand corner of entry */ 2488 int width, /* width of entry */ 2489 int height) /* height of entry */ 2490{ 2491 if ((mePtr->type == CHECK_BUTTON_ENTRY) || 2492 (mePtr->type == RADIO_BUTTON_ENTRY)) { 2493 if (mePtr->indicatorOn && (mePtr->entryFlags & ENTRY_SELECTED)) { 2494 short mark; 2495 int baseline = y + (height + fmPtr->ascent - fmPtr->descent)/2; 2496 2497 GetItemMark(((MacMenu *) menuPtr->platformData)->menuHdl, 2498 mePtr->index + 1, &mark); 2499 if (IS_THEME_MENU_FONT(tkfont)) { 2500 ThemeFontID font = kThemeMenuItemMarkFont; 2501 TextEncoding encoding = GetApplicationTextEncoding(); 2502 CFStringRef cfStr; 2503 ThemeDrawState drawState; 2504 Rect bounds = {y, x + menuMarkIndent, y + height, x + width}; 2505 2506 if (mark < kSpaceCharCode) { 2507 font = kThemeMenuItemCmdKeyFont; 2508 encoding = kTextEncodingMacKeyboardGlyphs; 2509 } 2510 switch (mePtr->state) { 2511 case ENTRY_ACTIVE: 2512 drawState = kThemeStatePressed; 2513 break; 2514 case ENTRY_DISABLED: 2515 drawState = kThemeStateInactive; 2516 break; 2517 default: 2518 drawState = kThemeStateActive; 2519 break; 2520 } 2521 cfStr = CFStringCreateWithBytes(NULL, (UInt8*)&mark, 1, 2522 encoding, false); 2523 if (cfStr) { 2524 DrawThemeText(d, gc, cfStr, font, drawState, &bounds, 2525 baseline, teFlushDefault); 2526 CFRelease(cfStr); 2527 } 2528 } else if (mark != 0) { 2529 const char *markUtf = NULL; 2530 int len; 2531 2532 len = GetUtfMarkCharacter(mark, &markUtf); 2533 Tk_DrawChars(menuPtr->display, d, gc, tkfont, markUtf, len, 2534 x + menuMarkIndent, baseline); 2535 } 2536 } 2537 } 2538} 2539 2540#ifdef USE_TK_MDEF 2541/* 2542 *---------------------------------------------------------------------- 2543 * 2544 * DrawMenuBackground -- 2545 * 2546 * If Appearance is present, draws the Appearance background 2547 * 2548 * Results: 2549 * Nothing 2550 * 2551 * Side effects: 2552 * Commands are output to X to display the menu in its 2553 * current mode. 2554 * 2555 *---------------------------------------------------------------------- 2556 */ 2557void 2558DrawMenuBackground( 2559 TkMenu *menuPtr, 2560 Rect *menuRectPtr, /* The menu rect */ 2561 Drawable d) /* What we are drawing into */ 2562{ 2563 Tk_3DBorder border; 2564 2565 EraseMenuBackground(((MacMenu *) menuPtr->platformData)->menuHdl, 2566 menuRectPtr, ((MacDrawable*)d)->context); 2567 border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); 2568 Tk_Fill3DRectangle(menuPtr->tkwin, d, border, 2569 menuRectPtr->left, menuRectPtr->top, 2570 menuRectPtr->right - menuRectPtr->left, 2571 menuRectPtr->bottom - menuRectPtr->top, 0, TK_RELIEF_FLAT); 2572} 2573#endif /* USE_TK_MDEF */ 2574 2575/* 2576 *---------------------------------------------------------------------- 2577 * 2578 * DrawMenuEntryAccelerator -- 2579 * 2580 * This procedure draws the accelerator part of a menu. 2581 * 2582 * Results: 2583 * None. 2584 * 2585 * Side effects: 2586 * Commands are output to X to display the menu in its 2587 * current mode. 2588 * 2589 *---------------------------------------------------------------------- 2590 */ 2591 2592void 2593DrawMenuEntryAccelerator( 2594 TkMenu *menuPtr, /* The menu we are drawing */ 2595 TkMenuEntry *mePtr, /* The entry we are drawing */ 2596 Drawable d, /* The drawable we are drawing in */ 2597 GC gc, /* The gc to draw into */ 2598 Tk_Font tkfont, /* The precalculated font */ 2599 const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 2600 Tk_3DBorder activeBorder, /* border for menu background */ 2601 int x, /* The left side of the entry */ 2602 int y, /* The top of the entry */ 2603 int width, /* The width of the entry */ 2604 int height, /* The height of the entry */ 2605 int drawArrow) /* Whether or not to draw cascade arrow */ 2606{ 2607 if (mePtr->type != CASCADE_ENTRY && mePtr->accelLength > 0) { 2608 const char *accel = (mePtr->accelPtr == NULL) ? "" 2609 : Tcl_GetString(mePtr->accelPtr); 2610 EntryGeometry *geometryPtr = (EntryGeometry*)mePtr->platformEntryData; 2611 int leftEdge = x + width - geometryPtr->accelTextWidth; 2612 int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; 2613 2614 if (IS_THEME_MENU_FONT(tkfont)) { 2615 CFStringRef cfStr; 2616 ThemeDrawState drawState; 2617 2618 switch (mePtr->state) { 2619 case ENTRY_ACTIVE: 2620 drawState = kThemeStatePressed; 2621 break; 2622 case ENTRY_DISABLED: 2623 drawState = kThemeStateInactive; 2624 break; 2625 default: 2626 drawState = kThemeStateActive; 2627 break; 2628 } 2629 if ((mePtr->entryFlags & ENTRY_ACCEL_MASK) == 0) { 2630 leftEdge -= geometryPtr->modifierWidth; 2631 } 2632 if (geometryPtr->accelGlyph) { 2633 Rect bounds = {y, leftEdge, y + height, leftEdge + 2634 geometryPtr->accelTextWidth}; 2635 2636 cfStr = CFStringCreateWithBytes(NULL, 2637 (UInt8*)&geometryPtr->accelGlyph, 1, 2638 kTextEncodingMacKeyboardGlyphs, false); 2639 if (cfStr) { 2640 DrawThemeText(d, gc, cfStr, kThemeMenuItemCmdKeyFont, 2641 drawState, &bounds, baseline, teFlushDefault); 2642 CFRelease(cfStr); 2643 } 2644 } else { 2645 Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel + 2646 geometryPtr->accelTextStart, mePtr->accelLength - 2647 geometryPtr->accelTextStart, leftEdge, baseline); 2648 } 2649 if (geometryPtr->modifierNum) { 2650 Rect bounds = {y, leftEdge - geometryPtr->modifierWidth, 2651 y + height, leftEdge}; 2652 2653 cfStr = CFStringCreateWithCharacters(NULL, 2654 geometryPtr->modifierUniChars, 2655 geometryPtr->modifierNum); 2656 if (cfStr) { 2657 DrawThemeText(d, gc, cfStr, kThemeMenuItemCmdKeyFont, 2658 drawState, &bounds, baseline, teFlushDefault); 2659 CFRelease(cfStr); 2660 } 2661 } 2662 } else { 2663 Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel, 2664 mePtr->accelLength, leftEdge, baseline); 2665 } 2666 } 2667} 2668 2669/* 2670 *---------------------------------------------------------------------- 2671 * 2672 * DrawMenuSeparator -- 2673 * 2674 * The menu separator is drawn. 2675 * 2676 * Results: 2677 * None. 2678 * 2679 * Side effects: 2680 * Commands are output to X to display the menu in its 2681 * current mode. 2682 * 2683 *---------------------------------------------------------------------- 2684 */ 2685 2686void 2687DrawMenuSeparator( 2688 TkMenu *menuPtr, /* The menu we are drawing */ 2689 TkMenuEntry *mePtr, /* The entry we are drawing */ 2690 Drawable d, /* The drawable we are drawing into */ 2691 GC gc, /* The gc we are drawing with */ 2692 Tk_Font tkfont, /* The precalculated font */ 2693 const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 2694 int x, /* left coordinate of entry */ 2695 int y, /* top coordinate of entry */ 2696 int width, /* width of entry */ 2697 int height) /* height of entry */ 2698{ 2699 TkMacOSXDrawingContext dc; 2700 Rect r; 2701 2702 r.top = y; 2703 r.left = x; 2704 r.bottom = y + height; 2705 r.right = x + width; 2706 if (TkMacOSXSetupDrawingContext(d, gc, 1, &dc)) { 2707 ChkErr(DrawThemeMenuSeparator, &r); 2708 TkMacOSXRestoreDrawingContext(&dc); 2709 } 2710} 2711 2712#ifdef USE_TK_MDEF 2713/* 2714 *---------------------------------------------------------------------- 2715 * 2716 * AppearanceEntryDrawWrapper -- 2717 * 2718 * It routes to the Appearance Managers DrawThemeEntry, which will 2719 * then call us back after setting up the drawing context. 2720 * 2721 * Results: 2722 * A menu entry is drawn 2723 * 2724 * Side effects: 2725 * None 2726 * 2727 *---------------------------------------------------------------------- 2728 */ 2729void 2730AppearanceEntryDrawWrapper( 2731 TkMenuEntry *mePtr, 2732 Rect *menuRectPtr, 2733 MenuTrackingData *mtdPtr, 2734 Drawable d, 2735 Tk_FontMetrics *fmPtr, 2736 Tk_Font tkfont, 2737 int erase) 2738{ 2739 MenuEntryUserData meData; 2740 Rect itemRect; 2741 ThemeMenuState theState; 2742 ThemeMenuItemType theType; 2743 Tk_FontMetrics entryMetrics; 2744 2745 meData.mePtr = mePtr; 2746 meData.mdefDrawable = d; 2747 if (mePtr->fontPtr == NULL) { 2748 meData.fmPtr = fmPtr; 2749 meData.tkfont = tkfont; 2750 } else { 2751 meData.tkfont = Tk_GetFontFromObj(mePtr->menuPtr->tkwin, 2752 mePtr->fontPtr); 2753 Tk_GetFontMetrics(meData.tkfont, &entryMetrics); 2754 fmPtr = &entryMetrics; 2755 } 2756 itemRect.left = menuRectPtr->left + mePtr->x; 2757 itemRect.top = mtdPtr->virtualMenuTop + mePtr->y; 2758 itemRect.right = mePtr->entryFlags & ENTRY_LAST_COLUMN ? 2759 menuRectPtr->right : itemRect.left + mePtr->width; 2760 itemRect.bottom = itemRect.top + mePtr->height; 2761 2762 if (mePtr->state == ENTRY_ACTIVE) { 2763 theState = kThemeMenuSelected; 2764 } else if (mePtr->state == ENTRY_DISABLED) { 2765 theState = kThemeMenuDisabled; 2766 } else { 2767 theState = kThemeMenuActive; 2768 } 2769 if (mePtr->type == CASCADE_ENTRY) { 2770 theType = kThemeMenuItemHierarchical; 2771 } else { 2772 theType = kThemeMenuItemPlain; 2773 } 2774 if (erase) { 2775 DisableScreenUpdates(); 2776 DrawMenuBackground(mePtr->menuPtr, &itemRect, d); 2777 } 2778 DrawThemeMenuItem(menuRectPtr, &itemRect, 2779 mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, theState, 2780 theType | kThemeMenuItemNoBackground, tkThemeMenuItemDrawingUPP, 2781 (unsigned long) &meData); 2782 if (erase) { 2783 EnableScreenUpdates(); 2784 } 2785} 2786 2787/* 2788 *---------------------------------------------------------------------- 2789 * 2790 * ThemeMenuItemDrawingProc -- 2791 * 2792 * This routine is called from the Appearance DrawThemeMenuEntry 2793 * 2794 * Results: 2795 * A menu entry is drawn 2796 * 2797 * Side effects: 2798 * None 2799 * 2800 *---------------------------------------------------------------------- 2801 */ 2802pascal void 2803ThemeMenuItemDrawingProc( 2804 const Rect *inBounds, 2805 SInt16 inDepth, 2806 Boolean inIsColorDevice, 2807 SInt32 inUserData) 2808{ 2809 MenuEntryUserData *meData = (MenuEntryUserData *) inUserData; 2810 2811 TkpDrawMenuEntry(meData->mePtr, meData->mdefDrawable, meData->tkfont, 2812 meData->fmPtr, inBounds->left, inBounds->top, inBounds->right - 2813 inBounds->left + menuItemExtraWidth, inBounds->bottom - 2814 inBounds->top + menuItemExtraHeight, 0, 1); 2815} 2816#endif /* USE_TK_MDEF */ 2817 2818/* 2819 *---------------------------------------------------------------------- 2820 * 2821 * TkMacOSXHandleTearoffMenu() -- 2822 * 2823 * This routine sees if the MDEF has set a menu and a mouse position 2824 * for tearing off and makes a tearoff menu if it has. 2825 * 2826 * Results: 2827 * menuPtr->interp will have the result of the tearoff command. 2828 * 2829 * Side effects: 2830 * A new tearoff menu is created if it is supposed to be. 2831 * 2832 *---------------------------------------------------------------------- 2833 */ 2834 2835void 2836TkMacOSXHandleTearoffMenu(void) 2837{ 2838 /* 2839 * Obsolete: Nothing to do. 2840 */ 2841} 2842 2843/* 2844 *-------------------------------------------------------------- 2845 * 2846 * TkpInitializeMenuBindings -- 2847 * 2848 * For every interp, initializes the bindings for Windows 2849 * menus. Does nothing on Mac or XWindows. 2850 * 2851 * Results: 2852 * None. 2853 * 2854 * Side effects: 2855 * C-level bindings are setup for the interp which will 2856 * handle Alt-key sequences for menus without beeping 2857 * or interfering with user-defined Alt-key bindings. 2858 * 2859 *-------------------------------------------------------------- 2860 */ 2861 2862void 2863TkpInitializeMenuBindings( 2864 Tcl_Interp *interp, /* The interpreter to set. */ 2865 Tk_BindingTable bindingTable) 2866 /* The table to add to. */ 2867{ 2868 /* 2869 * Nothing to do. 2870 */ 2871} 2872 2873/* 2874 *-------------------------------------------------------------- 2875 * 2876 * TkpComputeMenubarGeometry -- 2877 * 2878 * This procedure is invoked to recompute the size and 2879 * layout of a menu that is a menubar clone. 2880 * 2881 * Results: 2882 * None. 2883 * 2884 * Side effects: 2885 * Fields of menu entries are changed to reflect their 2886 * current positions, and the size of the menu window 2887 * itself may be changed. 2888 * 2889 *-------------------------------------------------------------- 2890 */ 2891 2892void 2893TkpComputeMenubarGeometry( 2894 TkMenu *menuPtr) /* Structure describing menu. */ 2895{ 2896 TkpComputeStandardMenuGeometry(menuPtr); 2897} 2898 2899/* 2900 *---------------------------------------------------------------------- 2901 * 2902 * DrawTearoffEntry -- 2903 * 2904 * This procedure draws a tearoff entry. 2905 * 2906 * Results: 2907 * None. 2908 * 2909 * Side effects: 2910 * Commands are output to X to display the menu in its 2911 * current mode. 2912 * 2913 *---------------------------------------------------------------------- 2914 */ 2915 2916void 2917DrawTearoffEntry( 2918 TkMenu *menuPtr, /* The menu we are drawing */ 2919 TkMenuEntry *mePtr, /* The entry we are drawing */ 2920 Drawable d, /* The drawable we are drawing into */ 2921 GC gc, /* The gc we are drawing with */ 2922 Tk_Font tkfont, /* The font we are drawing with */ 2923 const Tk_FontMetrics *fmPtr,/* The metrics we are drawing with */ 2924 int x, /* Left edge of entry. */ 2925 int y, /* Top edge of entry. */ 2926 int width, /* Width of entry. */ 2927 int height) /* Height of entry. */ 2928{ 2929 XPoint points[2]; 2930 int margin, segmentWidth, maxX; 2931 Tk_3DBorder border; 2932 2933 if (menuPtr->menuType != MASTER_MENU ) { 2934 return; 2935 } 2936 2937 margin = fmPtr->linespace/2; 2938 points[0].x = x; 2939 points[0].y = y + height/2; 2940 points[1].y = points[0].y; 2941 segmentWidth = 6; 2942 maxX = x + menuPtr->totalWidth - 1; 2943 border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); 2944 2945 while (points[0].x < maxX) { 2946 points[1].x = points[0].x + segmentWidth; 2947 if (points[1].x > maxX) { 2948 points[1].x = maxX; 2949 } 2950 Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, 2951 TK_RELIEF_RAISED); 2952 points[0].x += 2*segmentWidth; 2953 } 2954} 2955 2956/* 2957 *---------------------------------------------------------------------- 2958 * 2959 * TkMacOSXSetHelpMenuItemCount -- 2960 * 2961 * Has to be called after the first call to InsertMenu. Sets 2962 * up the global variable for the number of items in the 2963 * unmodified help menu. 2964 * NB. Nobody uses this any more, since you can get the number 2965 * of system help items from HMGetHelpMenu trivially. 2966 * But it is in the stubs table... 2967 * 2968 * Results: 2969 * None. 2970 * 2971 * Side effects: 2972 * Nothing. 2973 * 2974 *---------------------------------------------------------------------- 2975 */ 2976 2977void 2978TkMacOSXSetHelpMenuItemCount(void) 2979{ 2980 /* 2981 * Obsolete: Nothing to do. 2982 */ 2983} 2984 2985/* 2986 *---------------------------------------------------------------------- 2987 * 2988 * TkMacOSXMenuClick -- 2989 * 2990 * Prepares a menubar for MenuSelect or MenuKey. 2991 * 2992 * Results: 2993 * None. 2994 * 2995 * Side effects: 2996 * Any pending configurations of the menubar are completed. 2997 * 2998 *---------------------------------------------------------------------- 2999 */ 3000 3001void 3002TkMacOSXMenuClick(void) 3003{ 3004 TkMenu *menuPtr; 3005 TkMenuReferences *menuRefPtr; 3006 3007 if ((currentMenuBarInterp != NULL) && (currentMenuBarName != NULL)) { 3008 menuRefPtr = TkFindMenuReferences(currentMenuBarInterp, 3009 currentMenuBarName); 3010 for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr; 3011 menuPtr != NULL; menuPtr = menuPtr->nextInstancePtr) { 3012 if (menuPtr->menuType == MENUBAR) { 3013 CompleteIdlers(menuPtr); 3014 break; 3015 } 3016 } 3017 } 3018 3019 if (menuBarFlags & MENUBAR_REDRAW_PENDING) { 3020 Tcl_CancelIdleCall(DrawMenuBarWhenIdle, NULL); 3021 DrawMenuBarWhenIdle(NULL); 3022 } 3023} 3024 3025/* 3026 *---------------------------------------------------------------------- 3027 * 3028 * TkpDrawMenuEntry -- 3029 * 3030 * Draws the given menu entry at the given coordinates with the 3031 * given attributes. 3032 * 3033 * Results: 3034 * None. 3035 * 3036 * Side effects: 3037 * X Server commands are executed to display the menu entry. 3038 * 3039 *---------------------------------------------------------------------- 3040 */ 3041 3042void 3043TkpDrawMenuEntry( 3044 TkMenuEntry *mePtr, /* The entry to draw */ 3045 Drawable d, /* What to draw into */ 3046 Tk_Font tkfont, /* Precalculated font for menu */ 3047 const Tk_FontMetrics *menuMetricsPtr, 3048 /* Precalculated metrics for menu */ 3049 int x, /* X-coordinate of topleft of entry */ 3050 int y, /* Y-coordinate of topleft of entry */ 3051 int width, /* Width of the entry rectangle */ 3052 int height, /* Height of the current rectangle */ 3053 int strictMotif, /* Boolean flag */ 3054 int drawArrow) /* Whether or not to draw the cascade 3055 * arrow for cascade items. Only applies 3056 * to Windows. */ 3057{ 3058 GC gc; 3059 TkMenu *menuPtr = mePtr->menuPtr; 3060 int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0; 3061 GC indicatorGC; 3062 Tk_3DBorder bgBorder, activeBorder; 3063 const Tk_FontMetrics *fmPtr; 3064 Tk_FontMetrics entryMetrics; 3065 int adjustedY = y + padY; 3066 int adjustedHeight = height - 2 * padY; 3067 3068 /* 3069 * Choose the gc for drawing the foreground part of the entry. 3070 * Under Appearance, we pass a null (appearanceGC) to tell 3071 * ourselves not to change whatever color the appearance manager has set. 3072 */ 3073 3074 if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) { 3075 gc = mePtr->activeGC; 3076 if (gc == NULL) { 3077 gc = menuPtr->activeGC; 3078 } 3079 } else { 3080 TkMenuEntry *parentEntryPtr = GetParentMenuEntry(menuPtr); 3081 3082 if (((parentEntryPtr && parentEntryPtr->state == ENTRY_DISABLED) || 3083 (mePtr->state == ENTRY_DISABLED)) && 3084 (menuPtr->disabledFgPtr != NULL)) { 3085 gc = mePtr->disabledGC; 3086 if (gc == NULL) { 3087 gc = menuPtr->disabledGC; 3088 } 3089 } else { 3090 gc = mePtr->textGC; 3091 if (gc == NULL) { 3092 gc = menuPtr->textGC; 3093 } 3094 } 3095 } 3096 3097 indicatorGC = mePtr->indicatorGC; 3098 if (indicatorGC == NULL) { 3099 indicatorGC = menuPtr->indicatorGC; 3100 } 3101 3102 bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, 3103 (mePtr->borderPtr == NULL) 3104 ? menuPtr->borderPtr : mePtr->borderPtr); 3105 if (strictMotif) { 3106 activeBorder = bgBorder; 3107 } else { 3108 activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, 3109 (mePtr->activeBorderPtr == NULL) 3110 ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr); 3111 } 3112 3113 if (mePtr->fontPtr == NULL) { 3114 fmPtr = menuMetricsPtr; 3115 } else { 3116 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); 3117 Tk_GetFontMetrics(tkfont, &entryMetrics); 3118 fmPtr = &entryMetrics; 3119 } 3120 3121 /* 3122 * Need to draw the entire background, including padding. On Unix, 3123 * for menubars, we have to draw the rest of the entry taking 3124 * into account the padding. 3125 */ 3126 3127 DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, bgBorder, x, y, 3128 width, height); 3129 3130 if (mePtr->type == SEPARATOR_ENTRY) { 3131 DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, 3132 fmPtr, x, adjustedY, width, adjustedHeight); 3133 } else if (mePtr->type == TEAROFF_ENTRY) { 3134 DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY, 3135 width, adjustedHeight); 3136 } else { 3137 DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, 3138 adjustedY, width, adjustedHeight); 3139 DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, 3140 activeBorder, x, adjustedY, width, adjustedHeight, drawArrow); 3141 if (!mePtr->hideMargin) { 3142 DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, 3143 fmPtr, x, adjustedY, width, adjustedHeight); 3144 } 3145 } 3146} 3147 3148/* 3149 *-------------------------------------------------------------- 3150 * 3151 * TkpComputeStandardMenuGeometry -- 3152 * 3153 * This procedure is invoked to recompute the size and 3154 * layout of a menu that is not a menubar clone. 3155 * 3156 * Results: 3157 * None. 3158 * 3159 * Side effects: 3160 * Fields of menu entries are changed to reflect their 3161 * current positions, and the size of the menu window 3162 * itself may be changed. 3163 * 3164 *-------------------------------------------------------------- 3165 */ 3166 3167void 3168TkpComputeStandardMenuGeometry( 3169 TkMenu *menuPtr) /* Structure describing menu. */ 3170{ 3171 Tk_Font tkfont, menuFont; 3172 Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; 3173 int x, y, height, modifierWidth, labelWidth, indicatorSpace; 3174 int windowWidth, windowHeight, accelWidth, maxAccelTextWidth; 3175 int i, j, lastColumnBreak, maxModifierWidth, maxWidth, nonAccelMargin; 3176 int maxNonAccelMargin, maxEntryWithAccelWidth, maxEntryWithoutAccelWidth; 3177 int entryWidth, maxIndicatorSpace, borderWidth, activeBorderWidth; 3178 TkMenuEntry *mePtr, *columnEntryPtr; 3179 EntryGeometry *geometryPtr; 3180 int haveAccel = 0; 3181 3182 if (menuPtr->tkwin == NULL) { 3183 return; 3184 } 3185 3186 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 3187 &borderWidth); 3188 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, 3189 &activeBorderWidth); 3190 x = y = borderWidth; 3191 indicatorSpace = labelWidth = accelWidth = maxAccelTextWidth = 0; 3192 windowHeight = windowWidth = maxWidth = lastColumnBreak = 0; 3193 maxModifierWidth = nonAccelMargin = maxNonAccelMargin = 0; 3194 maxEntryWithAccelWidth = maxEntryWithoutAccelWidth = 0; 3195 maxIndicatorSpace = 0; 3196 3197 /* 3198 * On the Mac especially, getting font metrics can be quite slow, 3199 * so we want to do it intelligently. We are going to precalculate 3200 * them and pass them down to all of the measuring and drawing 3201 * routines. We will measure the font metrics of the menu once. 3202 * If an entry does not have its own font set, then we give 3203 * the geometry/drawing routines the menu's font and metrics. 3204 * If an entry has its own font, we will measure that font and 3205 * give all of the geometry/drawing the entry's font and metrics. 3206 */ 3207 3208 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 3209 Tk_GetFontMetrics(menuFont, &menuMetrics); 3210 3211 for (i = 0; i < menuPtr->numEntries; i++) { 3212 mePtr = menuPtr->entries[i]; 3213 if (mePtr->type == CASCADE_ENTRY || mePtr->accelLength > 0) { 3214 haveAccel = 1; 3215 break; 3216 } 3217 } 3218 3219 for (i = 0; i < menuPtr->numEntries; i++) { 3220 mePtr = menuPtr->entries[i]; 3221 if (mePtr->fontPtr == NULL) { 3222 tkfont = menuFont; 3223 fmPtr = &menuMetrics; 3224 } else { 3225 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); 3226 Tk_GetFontMetrics(tkfont, &entryMetrics); 3227 fmPtr = &entryMetrics; 3228 } 3229 3230 if ((i > 0) && mePtr->columnBreak) { 3231 if (maxIndicatorSpace != 0) { 3232 maxIndicatorSpace += 2; 3233 } 3234 for (j = lastColumnBreak; j < i; j++) { 3235 columnEntryPtr = menuPtr->entries[j]; 3236 geometryPtr = 3237 (EntryGeometry *) columnEntryPtr->platformEntryData; 3238 3239 columnEntryPtr->indicatorSpace = maxIndicatorSpace; 3240 columnEntryPtr->width = maxIndicatorSpace + maxWidth 3241 + 2 * activeBorderWidth; 3242 geometryPtr->accelTextWidth = maxAccelTextWidth; 3243 geometryPtr->modifierWidth = maxModifierWidth; 3244 columnEntryPtr->x = x; 3245 columnEntryPtr->entryFlags &= ~ENTRY_LAST_COLUMN; 3246 if (maxEntryWithoutAccelWidth > maxEntryWithAccelWidth) { 3247 geometryPtr->nonAccelMargin = maxEntryWithoutAccelWidth 3248 - maxEntryWithAccelWidth; 3249 if (geometryPtr->nonAccelMargin > maxNonAccelMargin) { 3250 geometryPtr->nonAccelMargin = maxNonAccelMargin; 3251 } 3252 } else { 3253 geometryPtr->nonAccelMargin = 0; 3254 } 3255 } 3256 x += maxIndicatorSpace + maxWidth + 2 * borderWidth; 3257 windowWidth = x; 3258 maxWidth = maxIndicatorSpace = maxAccelTextWidth = 0; 3259 maxModifierWidth = maxNonAccelMargin = maxEntryWithAccelWidth = 0; 3260 maxEntryWithoutAccelWidth = 0; 3261 lastColumnBreak = i; 3262 y = borderWidth; 3263 } 3264 geometryPtr = (EntryGeometry *) mePtr->platformEntryData; 3265 3266 if (mePtr->type == SEPARATOR_ENTRY) { 3267 GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, 3268 fmPtr, &entryWidth, &height); 3269 mePtr->height = height; 3270 } else if (mePtr->type == TEAROFF_ENTRY) { 3271 GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, 3272 fmPtr, &entryWidth, &height); 3273 mePtr->height = height; 3274 } else { 3275 /* 3276 * For each entry, compute the height required by that 3277 * particular entry, plus three widths: the width of the 3278 * label, the width to allow for an indicator to be displayed 3279 * to the left of the label (if any), and the width of the 3280 * accelerator to be displayed to the right of the label 3281 * (if any). These sizes depend, of course, on the type 3282 * of the entry. 3283 */ 3284 3285 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &labelWidth, &height); 3286 mePtr->height = height; 3287 3288 nonAccelMargin = 0; 3289 if (mePtr->type == CASCADE_ENTRY) { 3290 GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr, 3291 &modifierWidth, &accelWidth, &height); 3292 } else if (mePtr->accelLength == 0) { 3293 if (haveAccel && !mePtr->hideMargin) { 3294 if (IS_THEME_MENU_FONT(tkfont)) { 3295 nonAccelMargin = menuSymbols[COMMAND_SYMBOL].width; 3296 } else { 3297 nonAccelMargin = Tk_TextWidth(tkfont, 3298 menuSymbols[COMMAND_SYMBOL].utf, 3299 menuSymbols[COMMAND_SYMBOL].utfLen); 3300 } 3301 } 3302 accelWidth = modifierWidth = 0; 3303 } else { 3304 GetMenuAccelGeometry(menuPtr, mePtr, tkfont, 3305 fmPtr, &modifierWidth, &accelWidth, &height); 3306 if (height > mePtr->height) { 3307 mePtr->height = height; 3308 } 3309 } 3310 3311 if (!(mePtr->hideMargin)) { 3312 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, 3313 fmPtr, &indicatorSpace, &height); 3314 if (height > mePtr->height) { 3315 mePtr->height = height; 3316 } 3317 } else { 3318 indicatorSpace = 0; 3319 } 3320 3321 if (nonAccelMargin > maxNonAccelMargin) { 3322 maxNonAccelMargin = nonAccelMargin; 3323 } 3324 if (accelWidth > maxAccelTextWidth) { 3325 maxAccelTextWidth = accelWidth; 3326 } 3327 if (modifierWidth > maxModifierWidth) { 3328 maxModifierWidth = modifierWidth; 3329 } 3330 if (indicatorSpace > maxIndicatorSpace) { 3331 maxIndicatorSpace = indicatorSpace; 3332 } 3333 3334 entryWidth = labelWidth + modifierWidth + accelWidth 3335 + nonAccelMargin; 3336 3337 if (entryWidth > maxWidth) { 3338 maxWidth = entryWidth; 3339 } 3340 3341 if (mePtr->accelLength > 0) { 3342 if (entryWidth > maxEntryWithAccelWidth) { 3343 maxEntryWithAccelWidth = entryWidth; 3344 } 3345 } else { 3346 if (entryWidth > maxEntryWithoutAccelWidth) { 3347 maxEntryWithoutAccelWidth = entryWidth; 3348 } 3349 } 3350 mePtr->height += 2 * activeBorderWidth; 3351 } 3352 mePtr->y = y; 3353 y += menuPtr->entries[i]->height + borderWidth; 3354 if (y > windowHeight) { 3355 windowHeight = y; 3356 } 3357 } 3358 3359 for (j = lastColumnBreak; j < menuPtr->numEntries; j++) { 3360 columnEntryPtr = menuPtr->entries[j]; 3361 geometryPtr = (EntryGeometry *) columnEntryPtr->platformEntryData; 3362 3363 columnEntryPtr->indicatorSpace = maxIndicatorSpace; 3364 columnEntryPtr->width = maxIndicatorSpace + maxWidth 3365 + 2 * activeBorderWidth; 3366 geometryPtr->accelTextWidth = maxAccelTextWidth; 3367 columnEntryPtr->x = x; 3368 columnEntryPtr->entryFlags |= ENTRY_LAST_COLUMN; 3369 if (maxEntryWithoutAccelWidth > maxEntryWithAccelWidth) { 3370 geometryPtr->nonAccelMargin = maxEntryWithoutAccelWidth 3371 - maxEntryWithAccelWidth; 3372 if (geometryPtr->nonAccelMargin > maxNonAccelMargin) { 3373 geometryPtr->nonAccelMargin = maxNonAccelMargin; 3374 } 3375 } else { 3376 geometryPtr->nonAccelMargin = 0; 3377 } 3378 } 3379 windowWidth = x + maxIndicatorSpace + maxWidth 3380 + 2 * activeBorderWidth + borderWidth; 3381 windowHeight += borderWidth; 3382 3383 /* 3384 * The X server doesn't like zero dimensions, so round up to at least 3385 * 1 (a zero-sized menu should never really occur, anyway). 3386 */ 3387 3388 if (windowWidth <= 0) { 3389 windowWidth = 1; 3390 } 3391 if (windowHeight <= 0) { 3392 windowHeight = 1; 3393 } 3394 menuPtr->totalWidth = windowWidth; 3395 menuPtr->totalHeight = windowHeight; 3396} 3397 3398/* 3399 *---------------------------------------------------------------------- 3400 * 3401 * DrawMenuEntryLabel -- 3402 * 3403 * This procedure draws the label part of a menu. 3404 * 3405 * Results: 3406 * None. 3407 * 3408 * Side effects: 3409 * Commands are output to X to display the menu in its 3410 * current mode. 3411 * 3412 *---------------------------------------------------------------------- 3413 */ 3414 3415void 3416DrawMenuEntryLabel( 3417 TkMenu *menuPtr, /* The menu we are drawing */ 3418 TkMenuEntry *mePtr, /* The entry we are drawing */ 3419 Drawable d, /* What we are drawing into */ 3420 GC gc, /* The gc we are drawing into */ 3421 Tk_Font tkfont, /* The precalculated font */ 3422 const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ 3423 int x, /* left edge */ 3424 int y, /* right edge */ 3425 int width, /* width of entry */ 3426 int height) /* height of entry */ 3427{ 3428 int imageWidth, imageHeight, textWidth = 0, textHeight = 0; 3429 int indicatorSpace = mePtr->indicatorSpace; 3430 int leftEdge = x + indicatorSpace; 3431 int haveImage = 0, haveText = 0; 3432 int imageXOffset = 0, imageYOffset = 0; 3433 int textXOffset = 0, textYOffset = 0; 3434 Pixmap bitmap = (Pixmap) NULL; 3435 Tcl_DString itemTextDString; 3436 3437 /* 3438 * Work out what we will need to draw first. 3439 */ 3440 3441 if (mePtr->image != NULL) { 3442 Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); 3443 haveImage = 1; 3444 } else if (mePtr->bitmapPtr != NULL) { 3445 bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); 3446 Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight); 3447 haveImage = 1; 3448 } 3449 if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { 3450 if (mePtr->labelLength > 0) { 3451 GetEntryText(mePtr, &itemTextDString); 3452 if (mePtr->compound != COMPOUND_NONE) { 3453 textWidth = Tk_TextWidth(tkfont, 3454 Tcl_DStringValue(&itemTextDString), 3455 Tcl_DStringLength(&itemTextDString)) + 3456 menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin; 3457 textHeight = fmPtr->linespace; 3458 } 3459 haveText = 1; 3460 } 3461 } 3462 3463 /* 3464 * Now work out what the relative positions are. 3465 */ 3466 3467 if (haveImage && haveText && (mePtr->compound != COMPOUND_NONE)) { 3468 int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth); 3469 3470 switch ((enum compound) mePtr->compound) { 3471 case COMPOUND_TOP: 3472 textXOffset = (fullWidth - textWidth)/2; 3473 textYOffset = imageHeight/2 + 2; 3474 imageXOffset = (fullWidth - imageWidth)/2; 3475 imageYOffset = -textHeight/2; 3476 break; 3477 case COMPOUND_BOTTOM: 3478 textXOffset = (fullWidth - textWidth)/2; 3479 textYOffset = -imageHeight/2; 3480 imageXOffset = (fullWidth - imageWidth)/2; 3481 imageYOffset = textHeight/2 + 2; 3482 break; 3483 case COMPOUND_LEFT: 3484 /* 3485 * Position image in the indicator space to the left of the 3486 * entries, unless this entry is a radio|check button because 3487 * then the indicator space will be used. 3488 */ 3489 3490 textXOffset = imageWidth + 2 - menuTextLeadingEdgeMargin; 3491 if ((mePtr->type != CHECK_BUTTON_ENTRY) 3492 && (mePtr->type != RADIO_BUTTON_ENTRY)) { 3493 textXOffset -= indicatorSpace; 3494 imageXOffset = -indicatorSpace; 3495 } 3496 if (textXOffset < 0) { 3497 textXOffset = 0; 3498 } 3499 break; 3500 case COMPOUND_RIGHT: 3501 imageXOffset = textWidth + 2 - menuTextTrailingEdgeMargin; 3502 break; 3503 case COMPOUND_CENTER: 3504 textXOffset = (fullWidth - textWidth)/2; 3505 imageXOffset = (fullWidth - imageWidth)/2; 3506 break; 3507 case COMPOUND_NONE: 3508 /* 3509 * Never reached. 3510 */ 3511 break; 3512 } 3513 } 3514 3515 /* 3516 * Draw label and/or bitmap or image for entry. 3517 */ 3518 3519 if (mePtr->image != NULL) { 3520 if ((mePtr->selectImage != NULL) 3521 && (mePtr->entryFlags & ENTRY_SELECTED)) { 3522 Tk_RedrawImage(mePtr->selectImage, 0, 0, imageWidth, imageHeight, 3523 d, leftEdge + imageXOffset, 3524 y + (mePtr->height - imageHeight)/2 + imageYOffset); 3525 } else { 3526 Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, imageHeight, 3527 d, leftEdge + imageXOffset, 3528 y + (mePtr->height - imageHeight)/2 + imageYOffset); 3529 } 3530 } else if (mePtr->bitmapPtr != NULL) { 3531 XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, imageWidth, 3532 imageHeight, leftEdge + imageXOffset, 3533 y + (mePtr->height - imageHeight)/2 + imageYOffset, 1); 3534 } 3535 if (haveText) { 3536 int baseline = y + (height + fmPtr->ascent - fmPtr->descent)/2; 3537 3538 Tk_DrawChars(menuPtr->display, d, gc, tkfont, 3539 Tcl_DStringValue(&itemTextDString), 3540 Tcl_DStringLength(&itemTextDString), 3541 leftEdge + menuTextLeadingEdgeMargin + textXOffset, 3542 baseline + textYOffset); 3543 Tcl_DStringFree(&itemTextDString); 3544 } 3545 3546 if (mePtr->state == ENTRY_DISABLED) { 3547 if (menuPtr->disabledFgPtr == NULL) { 3548 /* XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y, 3549 width, height); */ 3550 } else if ((mePtr->image != NULL) 3551 && (menuPtr->disabledImageGC != None)) { 3552 XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC, 3553 leftEdge + imageXOffset, 3554 y + (mePtr->height - imageHeight)/2 + imageYOffset, 3555 imageWidth, imageHeight); 3556 } 3557 } 3558} 3559 3560/* 3561 *---------------------------------------------------------------------- 3562 * 3563 * DrawMenuEntryBackground -- 3564 * 3565 * This procedure draws the background part of a menu entry. 3566 * Under Appearance, we only draw the background if the entry's 3567 * border is set, we DO NOT inherit it from the menu... 3568 * 3569 * Results: 3570 * None. 3571 * 3572 * Side effects: 3573 * Commands are output to X to display the menu in its 3574 * current mode. 3575 * 3576 *---------------------------------------------------------------------- 3577 */ 3578 3579void 3580DrawMenuEntryBackground( 3581 TkMenu *menuPtr, /* The menu we are drawing. */ 3582 TkMenuEntry *mePtr, /* The entry we are drawing. */ 3583 Drawable d, /* What we are drawing into */ 3584 Tk_3DBorder activeBorder, /* Border for active items */ 3585 Tk_3DBorder bgBorder, /* Border for the background */ 3586 int x, /* left edge */ 3587 int y, /* top edge */ 3588 int width, /* width of rectangle to draw */ 3589 int height) /* height of rectangle to draw */ 3590{ 3591 if ((menuPtr->menuType == TEAROFF_MENU) 3592 || ((mePtr->state == ENTRY_ACTIVE) 3593 && (mePtr->activeBorderPtr != None)) 3594 || ((mePtr->state != ENTRY_ACTIVE) && (mePtr->borderPtr != None))) { 3595 if (mePtr->state == ENTRY_ACTIVE) { 3596 bgBorder = activeBorder; 3597 } 3598 Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, 3599 x, y, width, height, 0, TK_RELIEF_FLAT); 3600 } 3601} 3602 3603/* 3604 *---------------------------------------------------------------------- 3605 * 3606 * GetMenuLabelGeometry -- 3607 * 3608 * Figures out the size of the label portion of a menu item. 3609 * 3610 * Results: 3611 * widthPtr and heightPtr are filled in with the correct geometry 3612 * information. 3613 * 3614 * Side effects: 3615 * None. 3616 * 3617 *---------------------------------------------------------------------- 3618 */ 3619 3620void 3621GetMenuLabelGeometry( 3622 TkMenuEntry *mePtr, /* The entry we are computing */ 3623 Tk_Font tkfont, /* The precalculated font */ 3624 const Tk_FontMetrics *fmPtr,/* The precalculated metrics */ 3625 int *widthPtr, /* The resulting width of the label portion */ 3626 int *heightPtr) /* The resulting height of the label portion */ 3627{ 3628 TkMenu *menuPtr = mePtr->menuPtr; 3629 int haveImage = 0, tornOff = (menuPtr->menuType == TEAROFF_MENU); 3630#ifdef USE_TK_MDEF 3631 const int useMDEF = ((MacMenu *) menuPtr->platformData)->useMDEF; 3632#endif 3633 3634 if (mePtr->image != NULL && (useMDEF || tornOff)) { 3635 Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr); 3636 haveImage = 1; 3637 } else if (mePtr->bitmapPtr != NULL && (useMDEF || tornOff)) { 3638 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); 3639 Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr); 3640 haveImage = 1; 3641 } 3642 if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { 3643 int textWidth = 0, textHeight = fmPtr->linespace; 3644 3645 if (mePtr->labelPtr != NULL) { 3646 Tcl_DString itemTextDString; 3647 3648 GetEntryText(mePtr, &itemTextDString); 3649 textWidth = Tk_TextWidth(tkfont, 3650 Tcl_DStringValue(&itemTextDString), 3651 Tcl_DStringLength(&itemTextDString)) + 3652 menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin; 3653 Tcl_DStringFree(&itemTextDString); 3654 3655 if (haveImage && (mePtr->compound != COMPOUND_NONE)) { 3656 switch ((enum compound) mePtr->compound) { 3657 int margin; 3658 3659 case COMPOUND_TOP: 3660 case COMPOUND_BOTTOM: 3661 if (textWidth > *widthPtr) { 3662 *widthPtr = textWidth; 3663 } 3664 *heightPtr += textHeight + 2; 3665 break; 3666 case COMPOUND_LEFT: 3667 margin = *widthPtr + 2; 3668 if (margin > menuTextLeadingEdgeMargin) { 3669 margin = menuTextLeadingEdgeMargin; 3670 } 3671 *widthPtr += textWidth + 2 - margin; 3672 if (textHeight > *heightPtr) { 3673 *heightPtr = textHeight; 3674 } 3675 break; 3676 case COMPOUND_RIGHT: 3677 margin = menuTextTrailingEdgeMargin; 3678 *widthPtr += textWidth + 2 - margin; 3679 if (textHeight > *heightPtr) { 3680 *heightPtr = textHeight; 3681 } 3682 break; 3683 case COMPOUND_CENTER: 3684 if (textWidth > *widthPtr) { 3685 *widthPtr = textWidth; 3686 } 3687 if (textHeight > *heightPtr) { 3688 *heightPtr = textHeight; 3689 } 3690 break; 3691 case COMPOUND_NONE: 3692 /* 3693 * Never reached. 3694 */ 3695 break; 3696 } 3697 goto labelGeomDone; 3698 } 3699 } 3700 *widthPtr = textWidth; 3701 *heightPtr = textHeight; 3702 } 3703 3704labelGeomDone: 3705 *heightPtr += menuItemExtraHeight; 3706 *widthPtr += menuItemExtraWidth; 3707} 3708 3709/* 3710 *---------------------------------------------------------------------- 3711 * 3712 * TkMacOSXGenerateParentMenuSelectEvent -- 3713 * 3714 * Respond to a hierarchical menu being opened. 3715 * 3716 * Results: 3717 * True if event(s) are generated - false otherwise. 3718 * 3719 * Side effects: 3720 * Places a virtual event on the event queue. 3721 * 3722 *---------------------------------------------------------------------- 3723 */ 3724 3725int 3726TkMacOSXGenerateParentMenuSelectEvent( 3727 MenuRef menu) 3728{ 3729 TkMenu *menuPtr = MenuPtrForMenuRef(menu); 3730 3731 if (menuPtr) { 3732 TkMenuEntry *parentEntryPtr = GetParentMenuEntry(menuPtr); 3733 3734 if (parentEntryPtr && (menuPtr = parentEntryPtr->menuPtr)) { 3735 TkActivateMenuEntry(menuPtr, parentEntryPtr->index); 3736 MenuSelectEvent(menuPtr); 3737 Tcl_ServiceAll(); 3738 return true; 3739 } 3740 } 3741 return false; 3742} 3743 3744/* 3745 *---------------------------------------------------------------------- 3746 * 3747 * TkMacOSXGenerateMenuSelectEvent -- 3748 * 3749 * Respond to a menu item being selected. 3750 * 3751 * Results: 3752 * True if event(s) are generated - false otherwise. 3753 * 3754 * Side effects: 3755 * Places a virtual event on the event queue. 3756 * 3757 *---------------------------------------------------------------------- 3758 */ 3759 3760int 3761TkMacOSXGenerateMenuSelectEvent( 3762 MenuRef menu, 3763 MenuItemIndex index) 3764{ 3765 TkMenu *menuPtr = MenuPtrForMenuRef(menu); 3766 int item = index - 1; 3767 3768 if (menuPtr) { 3769 if (item < 0 || item >= menuPtr->numEntries || 3770 (menuPtr->entries[item])->state == ENTRY_DISABLED) { 3771 TkActivateMenuEntry(menuPtr, -1); 3772 } else { 3773 TkActivateMenuEntry(menuPtr, item); 3774 MenuSelectEvent(menuPtr); 3775 Tcl_ServiceAll(); 3776 return true; 3777 } 3778 } 3779 return false; 3780} 3781 3782/* 3783 *---------------------------------------------------------------------- 3784 * 3785 * MenuSelectEvent -- 3786 * 3787 * Generates a "MenuSelect" virtual event. This can be used to 3788 * do context-sensitive menu help. 3789 * 3790 * Results: 3791 * None. 3792 * 3793 * Side effects: 3794 * Places a virtual event on the event queue. 3795 * 3796 *---------------------------------------------------------------------- 3797 */ 3798 3799void 3800MenuSelectEvent( 3801 TkMenu *menuPtr) /* the menu we have selected. */ 3802{ 3803 XVirtualEvent event; 3804 3805 bzero(&event, sizeof(XVirtualEvent)); 3806 event.type = VirtualEvent; 3807 event.serial = menuPtr->display->request; 3808 event.send_event = false; 3809 event.display = menuPtr->display; 3810 Tk_MakeWindowExist(menuPtr->tkwin); 3811 event.event = Tk_WindowId(menuPtr->tkwin); 3812 event.root = XRootWindow(menuPtr->display, 0); 3813 event.subwindow = None; 3814 event.time = TkpGetMS(); 3815 3816 XQueryPointer(NULL, None, NULL, NULL, &event.x_root, &event.y_root, NULL, 3817 NULL, &event.state); 3818 event.same_screen = true; 3819 event.name = Tk_GetUid("MenuSelect"); 3820 Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); 3821} 3822 3823/* 3824 *---------------------------------------------------------------------- 3825 * 3826 * TkMacOSXClearActiveMenu -- 3827 * 3828 * Clears Tk's active entry for the given MenuRef. 3829 * 3830 * Results: 3831 * None. 3832 * 3833 * Side effects: 3834 * Generates <<MenuSelect>> virtual events. 3835 * 3836 *---------------------------------------------------------------------- 3837 */ 3838 3839void 3840TkMacOSXClearActiveMenu( 3841 MenuRef menu) 3842{ 3843 TkMenu *menuPtr = MenuPtrForMenuRef(menu); 3844 3845 if (menuPtr) { 3846 RecursivelyClearActiveMenu(menuPtr); 3847 } 3848} 3849 3850/* 3851 *---------------------------------------------------------------------- 3852 * 3853 * RecursivelyClearActiveMenu -- 3854 * 3855 * Recursively clears the active entry in the menu's cascade hierarchy. 3856 * 3857 * Results: 3858 * None. 3859 * 3860 * Side effects: 3861 * Generates <<MenuSelect>> virtual events. 3862 * 3863 *---------------------------------------------------------------------- 3864 */ 3865 3866void 3867RecursivelyClearActiveMenu( 3868 TkMenu *menuPtr) /* The menu to reset. */ 3869{ 3870 int i; 3871 TkMenuEntry *mePtr; 3872 3873 TkActivateMenuEntry(menuPtr, -1); 3874 for (i = 0; i < menuPtr->numEntries; i++) { 3875 mePtr = menuPtr->entries[i]; 3876 if (mePtr->type == CASCADE_ENTRY) { 3877 if ((mePtr->childMenuRefPtr != NULL) 3878 && (mePtr->childMenuRefPtr->menuPtr != NULL)) { 3879 RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr); 3880 } 3881 } 3882 } 3883} 3884 3885/* 3886 *---------------------------------------------------------------------- 3887 * 3888 * TkMacOSXClearMenubarActive -- 3889 * 3890 * Recursively clears the active entry in the current menubar hierarchy. 3891 * 3892 * Results: 3893 * None. 3894 * 3895 * Side effects: 3896 * Generates <<MenuSelect>> virtual events. 3897 * 3898 *---------------------------------------------------------------------- 3899 */ 3900 3901void 3902TkMacOSXClearMenubarActive(void) 3903{ 3904 TkMenuReferences *menuBarRefPtr; 3905 3906 if (currentMenuBarName != NULL) { 3907 menuBarRefPtr = TkFindMenuReferences(currentMenuBarInterp, 3908 currentMenuBarName); 3909 if ((menuBarRefPtr != NULL) && (menuBarRefPtr->menuPtr != NULL)) { 3910 TkMenu *menuPtr; 3911 3912 for (menuPtr = menuBarRefPtr->menuPtr->masterMenuPtr; 3913 menuPtr != NULL; menuPtr = menuPtr->nextInstancePtr) { 3914 if (menuPtr->menuType == MENUBAR) { 3915 RecursivelyClearActiveMenu(menuPtr); 3916 } 3917 } 3918 } 3919 } 3920} 3921 3922/* 3923 *---------------------------------------------------------------------- 3924 * 3925 * TkpMenuNotifyToplevelCreate -- 3926 * 3927 * This routine reconfigures the menu and the clones indicated by 3928 * menuName becuase a toplevel has been created and any system 3929 * menus need to be created. Only applicable to Windows. 3930 * 3931 * Results: 3932 * None. 3933 * 3934 * Side effects: 3935 * An idle handler is set up to do the reconfiguration. 3936 * 3937 *---------------------------------------------------------------------- 3938 */ 3939 3940void 3941TkpMenuNotifyToplevelCreate( 3942 Tcl_Interp *interp, /* The interp the menu lives in. */ 3943 char *menuName) /* The name of the menu to reconfigure. */ 3944{ 3945 /* 3946 * Nothing to do. 3947 */ 3948} 3949 3950/* 3951 *---------------------------------------------------------------------- 3952 * 3953 * TkpMenuInit -- 3954 * 3955 * Initializes Mac-specific menu data. 3956 * 3957 * Results: 3958 * None. 3959 * 3960 * Side effects: 3961 * Allocates a hash table. 3962 * 3963 *---------------------------------------------------------------------- 3964 */ 3965 3966void 3967TkpMenuInit(void) 3968{ 3969 MenuSymbol *ms = menuSymbols; 3970 CFStringRef cfStr; 3971 3972 lastMenuID = 256; 3973 Tcl_InitHashTable(&commandTable, TCL_ONE_WORD_KEYS); 3974 currentMenuBarOwner = NULL; 3975 currentAppleMenuID = 0; 3976 currentHelpMenuID = 0; 3977 currentMenuBarInterp = NULL; 3978 currentMenuBarName = NULL; 3979 windowListPtr = NULL; 3980 3981#ifdef USE_TK_MDEF 3982 tkThemeMenuItemDrawingUPP 3983 = NewMenuItemDrawingUPP(ThemeMenuItemDrawingProc); 3984 useMDEFVar = Tcl_NewStringObj("::tk::mac::useCustomMDEF", -1); 3985 macMDEFDrawable.winPtr = NULL; 3986 macMDEFDrawable.xOff = 0; 3987 macMDEFDrawable.yOff = 0; 3988 macMDEFDrawable.visRgn = NULL; 3989 macMDEFDrawable.aboveVisRgn = NULL; 3990 macMDEFDrawable.drawRect = CGRectNull; 3991 macMDEFDrawable.referenceCount = 0; 3992 macMDEFDrawable.toplevel = NULL; 3993 macMDEFDrawable.flags = 0; 3994 macMDEFDrawable.grafPtr = NULL; 3995 macMDEFDrawable.context = NULL; 3996 macMDEFDrawable.size = CGSizeZero; 3997#endif 3998 3999 ChkErr(GetThemeMetric, kThemeMetricMenuMarkColumnWidth, 4000 &menuMarkColumnWidth); 4001 ChkErr(GetThemeMetric, kThemeMetricMenuMarkIndent, &menuMarkIndent); 4002 ChkErr(GetThemeMetric, kThemeMetricMenuTextLeadingEdgeMargin, 4003 &menuTextLeadingEdgeMargin); 4004 ChkErr(GetThemeMetric, kThemeMetricMenuTextTrailingEdgeMargin, 4005 &menuTextTrailingEdgeMargin); 4006 ChkErr(GetThemeMenuItemExtra, kThemeMenuItemPlain, &menuItemExtraHeight, 4007 &menuItemExtraWidth); 4008 ChkErr(GetThemeMenuSeparatorHeight, &menuSeparatorHeight); 4009 4010 while (ms->unicode) { 4011 ms->utfLen = Tcl_UniCharToUtf(ms->unicode, ms->utf); 4012 ms->utf[ms->utfLen] = 0; 4013 cfStr = CFStringCreateWithCharacters(NULL, &ms->unicode, 1); 4014 if (cfStr) { 4015 ms->width = MeasureThemeText(cfStr, kThemeMenuItemCmdKeyFont); 4016 CFRelease(cfStr); 4017 } 4018 ms++; 4019 } 4020} 4021 4022/* 4023 *---------------------------------------------------------------------- 4024 * 4025 * TkpMenuThreadInit -- 4026 * 4027 * Does platform-specific initialization of thread-specific 4028 * menu state. 4029 * 4030 * Results: 4031 * None. 4032 * 4033 * Side effects: 4034 * None. 4035 * 4036 *---------------------------------------------------------------------- 4037 */ 4038 4039void 4040TkpMenuThreadInit(void) 4041{ 4042 /* 4043 * Nothing to do. 4044 */ 4045} 4046 4047/* 4048 *---------------------------------------------------------------------- 4049 * 4050 * TkpPreprocessMacMenu -- 4051 * 4052 * Handle preprocessing of menubar if it exists. 4053 * 4054 * Results: 4055 * None. 4056 * 4057 * Side effects: 4058 * All post commands for the current menubar get executed. 4059 * 4060 *---------------------------------------------------------------------- 4061 */ 4062 4063void 4064TkMacOSXPreprocessMenu(void) 4065{ 4066 if ((currentMenuBarName != NULL) && (currentMenuBarInterp != NULL)) { 4067 TkMenuReferences *mbRefPtr = 4068 TkFindMenuReferences(currentMenuBarInterp,currentMenuBarName); 4069 4070 if ((mbRefPtr != NULL) && (mbRefPtr->menuPtr != NULL)) { 4071 int code; 4072 4073 Tcl_Preserve((ClientData) currentMenuBarInterp); 4074 code = TkPreprocessMenu(mbRefPtr->menuPtr->masterMenuPtr); 4075 if ((code != TCL_OK) && (code != TCL_CONTINUE) 4076 && (code != TCL_BREAK)) { 4077 Tcl_AddErrorInfo(currentMenuBarInterp, 4078 "\n (menu preprocess)"); 4079 Tcl_BackgroundError(currentMenuBarInterp); 4080 } 4081 Tcl_Release((ClientData) currentMenuBarInterp); 4082 } 4083 } 4084} 4085 4086#ifdef USE_TK_MDEF 4087#pragma mark MDEF 4088/* 4089 *---------------------------------------------------------------------- 4090 * 4091 * MenuDefProc -- 4092 * 4093 * This routine is the MDEF handler for Tk. It receives all messages 4094 * for the menu and dispatches them. 4095 * 4096 * Results: 4097 * None. 4098 * 4099 * Side effects: 4100 * This routine causes menus to be drawn and will certainly allocate 4101 * memory as a result. Also, the menu can scroll up and down, and 4102 * various other interface actions can take place. 4103 * 4104 *---------------------------------------------------------------------- 4105 */ 4106 4107void 4108MenuDefProc( 4109 SInt16 message, /* What action are we taking? */ 4110 MenuRef menu, /* The menu we are working with */ 4111 Rect *menuRectPtr, /* A pointer to the rect for the 4112 * whole menu. */ 4113 Point hitPt, /* Where the mouse was clicked for 4114 * the appropriate messages. */ 4115 SInt16 *whichItem) /* Output result. Which item was 4116 * hit by the user? */ 4117{ 4118 TkMenu *menuPtr; 4119 Tcl_HashEntry *commandEntryPtr; 4120 MenuID menuID; 4121 4122 menuID = GetMenuID(menu); 4123 commandEntryPtr = Tcl_FindHashEntry(&commandTable, (char*)(intptr_t)menuID); 4124 4125 if (!commandEntryPtr) return; 4126 menuPtr = (TkMenu *) Tcl_GetHashValue(commandEntryPtr); 4127 4128 switch (message) { 4129 case kMenuInitMsg: 4130 *whichItem = noErr; 4131 break; 4132 case kMenuDisposeMsg: 4133 break; 4134 case kMenuHiliteItemMsg: 4135 HandleMenuHiliteMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); 4136 break; 4137 case kMenuCalcItemMsg: 4138 HandleMenuCalcItemMsg(menu, menuRectPtr, hitPt, whichItem, 4139 menuPtr); 4140 break; 4141 case kMenuDrawItemsMsg: 4142#ifdef TK_MAC_DEBUG_MENUS 4143 TkMacOSXDbgMsg("MDEF: DrawItemsMsg"); 4144#endif 4145 /* 4146 * We do nothing here, because we don't support the Menu Managers 4147 * dynamic item groups 4148 */ 4149 break; 4150 case kMenuThemeSavvyMsg: 4151 *whichItem = kThemeSavvyMenuResponse; 4152 break; 4153 case kMenuSizeMsg: 4154#ifdef TK_MAC_DEBUG_MENUS 4155 TkMacOSXDbgMsg("MDEF: SizeMsg %d, %d", hitPt.h, hitPt.v); 4156#endif 4157 SetMenuWidth(menu, hitPt.h < menuPtr->totalWidth ? hitPt.h : 4158 menuPtr->totalWidth); 4159 SetMenuHeight(menu, hitPt.v < menuPtr->totalHeight ? hitPt.v : 4160 menuPtr->totalHeight); 4161 break; 4162 case kMenuDrawMsg: 4163 HandleMenuDrawMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); 4164 break; 4165 case kMenuFindItemMsg: 4166 HandleMenuFindItemMsg(menu, menuRectPtr, hitPt, whichItem, 4167 menuPtr); 4168 break; 4169 case kMenuPopUpMsg: 4170 HandleMenuPopUpMsg(menu, menuRectPtr, hitPt, whichItem, menuPtr); 4171 break; 4172 } 4173} 4174 4175/* 4176 *---------------------------------------------------------------------- 4177 * 4178 * HandleMenuHiliteMsg -- 4179 * 4180 * Handles the MenuDefProc's hilite message. 4181 * 4182 * Results: 4183 * A menu entry is drawn 4184 * 4185 * Side effects: 4186 * None 4187 * 4188 *---------------------------------------------------------------------- 4189 */ 4190 4191void 4192HandleMenuHiliteMsg( 4193 MenuRef menu, 4194 Rect *menuRectPtr, 4195 Point hitPt, 4196 SInt16 *whichItem, 4197 TkMenu *menuPtr) 4198{ 4199 OSStatus err; 4200 Tk_Font tkfont; 4201 Tk_FontMetrics fontMetrics; 4202 MDEFHiliteItemData *hidPtr = (MDEFHiliteItemData *)whichItem; 4203 int oldItem = hidPtr->previousItem - 1; 4204 int newItem = hidPtr->newItem - 1; 4205 MenuTrackingData mtd, *mtdPtr = &mtd; 4206 4207#ifdef TK_MAC_DEBUG_MENUS 4208 TkMacOSXDbgMsg("MDEF: HiliteMsg %d -> %d", hidPtr->previousItem, 4209 hidPtr->newItem); 4210#endif 4211 GetPort(&macMDEFDrawable.grafPtr); 4212 macMDEFDrawable.context = (CGContextRef) hidPtr->context; 4213 4214 err = ChkErr(GetMenuTrackingData, menu, mtdPtr); 4215 if (err != noErr) { 4216 return; 4217 } 4218 4219 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 4220 Tk_GetFontMetrics(tkfont, &fontMetrics); 4221 if (oldItem >= 0) { 4222 AppearanceEntryDrawWrapper(menuPtr->entries[oldItem], menuRectPtr, 4223 mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, tkfont, 1); 4224 } 4225 if (newItem >= 0) { 4226 AppearanceEntryDrawWrapper(menuPtr->entries[newItem], menuRectPtr, 4227 mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, tkfont, 0); 4228 } 4229} 4230 4231/* 4232 *---------------------------------------------------------------------- 4233 * 4234 * HandleMenuDrawMsg -- 4235 * 4236 * Handles the MenuDefProc's draw message. 4237 * 4238 * Results: 4239 * A menu entry is drawn 4240 * 4241 * Side effects: 4242 * None 4243 * 4244 *---------------------------------------------------------------------- 4245 */ 4246 4247void 4248HandleMenuDrawMsg( 4249 MenuRef menu, 4250 Rect *menuRectPtr, 4251 Point hitPt, 4252 SInt16 *whichItem, 4253 TkMenu *menuPtr) 4254{ 4255 Tk_Font menuFont; 4256 Tk_FontMetrics fontMetrics; 4257 TkMenuEntry *mePtr; 4258 int i; 4259 Rect menuClipRect, bounds; 4260 MDEFDrawData *ddPtr = (MDEFDrawData*)whichItem; 4261 MenuTrackingData *mtdPtr = &(ddPtr->trackingData); 4262 TkWindow *winPtr = (TkWindow*)menuPtr->tkwin; 4263 4264 GetPort(&macMDEFDrawable.grafPtr); 4265 GetPortBounds(macMDEFDrawable.grafPtr, &bounds); 4266 macMDEFDrawable.context = (CGContextRef) ddPtr->context; 4267#ifdef TK_MAC_DEBUG_MENUS 4268 TkMacOSXDbgMsg("MDEF: DrawMsg %d - %d; %d - %d", menuRectPtr->top, 4269 menuRectPtr->bottom, bounds.top, bounds.bottom); 4270#endif 4271 winPtr->changes.x = menuRectPtr->left; 4272 winPtr->changes.y = menuRectPtr->top; 4273 winPtr->changes.width = menuRectPtr->right - menuRectPtr->left; 4274 winPtr->changes.height = menuRectPtr->bottom - menuRectPtr->top; 4275 TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, 4276 0, 0, -1, -1); 4277#if 0 4278 if (menuPtr->menuRefPtr->topLevelListPtr != NULL) { 4279 menuType = kThemeMenuTypePullDown; 4280 } else if (menuPtr->menuRefPtr->parentEntryPtr != NULL) { 4281 menuType = kThemeMenuTypeHierarchical; 4282 } else { 4283 menuType = kThemeMenuTypePopUp; 4284 } 4285#endif 4286 DrawMenuBackground(menuPtr, menuRectPtr, (Drawable) &macMDEFDrawable); 4287 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 4288 Tk_GetFontMetrics(menuFont, &fontMetrics); 4289 menuClipRect = *menuRectPtr; 4290 mtdPtr->virtualMenuBottom = mtdPtr->virtualMenuTop + menuPtr->totalHeight; 4291 4292 /* 4293 * Next, figure out scrolling information. 4294 */ 4295 4296 if ((menuRectPtr->bottom - menuRectPtr->top) < menuPtr->totalHeight) { 4297 short arrowHeight = fontMetrics.linespace + 1; 4298 Rect arrowRect, eraseRect; 4299 ThemeMenuState menuState = IsMenuItemEnabled(menu, 0) ? 4300 kThemeMenuActive : kThemeMenuDisabled; 4301 4302 if (mtdPtr->virtualMenuTop < menuRectPtr->top) { 4303 arrowRect = bounds; 4304 /*arrowRect.top += 1;*/ 4305 arrowRect.bottom = arrowRect.top + arrowHeight; 4306 eraseRect = arrowRect; 4307 eraseRect.top = menuRectPtr->top; 4308 menuClipRect.top = arrowRect.bottom; 4309 ChkErr(EraseMenuBackground, menu, &eraseRect, 4310 macMDEFDrawable.context); 4311 ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, 4312 mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, 4313 menuState, kThemeMenuItemScrollUpArrow, NULL, 0); 4314#ifdef TK_MAC_DEBUG_MENUS 4315 TkMacOSXDbgMsg("upArrow: %d - %d, %d - %d", arrowRect.top, 4316 arrowRect.bottom, arrowRect.left, arrowRect.right); 4317#endif 4318 } 4319 if (mtdPtr->virtualMenuBottom > menuRectPtr->bottom) { 4320 arrowRect = bounds; 4321 arrowRect.bottom -= 1; 4322 arrowRect.top = arrowRect.bottom - arrowHeight; 4323 eraseRect = arrowRect; 4324 eraseRect.bottom = menuRectPtr->bottom; 4325 menuClipRect.bottom = arrowRect.top; 4326 ChkErr(EraseMenuBackground, menu, &eraseRect, 4327 macMDEFDrawable.context); 4328 ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, 4329 mtdPtr->virtualMenuTop, mtdPtr->virtualMenuBottom, 4330 menuState, kThemeMenuItemScrollDownArrow, NULL, 0); 4331#ifdef TK_MAC_DEBUG_MENUS 4332 TkMacOSXDbgMsg("downArrow: %d - %d, %d - %d", arrowRect.top, 4333 arrowRect.bottom, arrowRect.left, arrowRect.right); 4334#endif 4335 } 4336 TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, 4337 menuClipRect.left, menuClipRect.top, menuClipRect.right - 4338 menuClipRect.left, menuClipRect.bottom - menuClipRect.top); 4339 } 4340 4341 /* 4342 * Now, actually draw the menu. Don't draw entries that 4343 * are higher than the top arrow, and don't draw entries 4344 * that are lower than the bottom. 4345 */ 4346 4347 for (i = 0; i < menuPtr->numEntries; i++) { 4348 mePtr = menuPtr->entries[i]; 4349 if (mtdPtr->virtualMenuTop + mePtr->y + mePtr->height < 4350 menuClipRect.top || mtdPtr->virtualMenuTop + mePtr->y > 4351 menuClipRect.bottom) { 4352 continue; 4353 } 4354 AppearanceEntryDrawWrapper(mePtr, menuRectPtr, mtdPtr, 4355 (Drawable) &macMDEFDrawable, &fontMetrics, menuFont, 0); 4356 } 4357 MDEFScrollFlag = 1; 4358} 4359 4360/* 4361 *---------------------------------------------------------------------- 4362 * 4363 * HandleMenuFindItemMsg -- 4364 * 4365 * Handles the MenuDefProc's FindItems message. We have to 4366 * respond by filling in the itemSelected, itemUnderMouse and 4367 * itemRect fields. This is also the time to scroll the menu if 4368 * it is too long to fit on the screen. 4369 * 4370 * Results: 4371 * The Menu system is informed of the selected item & the item 4372 * under the mouse. 4373 * 4374 * Side effects: 4375 * The menu might get scrolled. 4376 * 4377 *---------------------------------------------------------------------- 4378 */ 4379void 4380HandleMenuFindItemMsg( 4381 MenuRef menu, 4382 Rect *menuRectPtr, 4383 Point hitPt, 4384 SInt16 *whichItem, 4385 TkMenu *menuPtr) 4386{ 4387 Tk_Font menuFont; 4388 Tk_FontMetrics fontMetrics; 4389 TkMenuEntry *mePtr; 4390 int i, newItem = -1, itemUnderMouse = -1; 4391 Rect itemRect = {0, 0, 0, 0}, menuClipRect, bounds; 4392 int hasTopScroll, hasBottomScroll; 4393 MDEFFindItemData *fiPtr = (MDEFFindItemData *)whichItem; 4394 MenuTrackingData *mtdPtr = &(fiPtr->trackingData), topMtd; 4395 enum { 4396 DONT_SCROLL, DOWN_SCROLL, UP_SCROLL 4397 } scrollDirection; 4398 short arrowHeight; 4399 4400#ifdef TK_MAC_DEBUG_MENUS 4401 static Point lastHitPt = {0, 0}; 4402 if (hitPt.h != lastHitPt.h || hitPt.v != lastHitPt.v) { 4403 lastHitPt = hitPt; 4404 TkMacOSXDbgMsg("MDEF: FindItemMsg: %d, %d", hitPt.h, hitPt.v); 4405 } 4406#endif 4407 4408 GetPort(&macMDEFDrawable.grafPtr); 4409 GetPortBounds(macMDEFDrawable.grafPtr, &bounds); 4410 macMDEFDrawable.context = (CGContextRef) fiPtr->context; 4411 4412 /* 4413 * Now we need to take care of scrolling the menu. 4414 */ 4415 4416 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 4417 Tk_GetFontMetrics(menuFont, &fontMetrics); 4418 arrowHeight = fontMetrics.linespace + 1; 4419 menuClipRect = *menuRectPtr; 4420 hasTopScroll = mtdPtr->virtualMenuTop < menuRectPtr->top; 4421 hasBottomScroll = mtdPtr->virtualMenuBottom > menuRectPtr->bottom; 4422 scrollDirection = DONT_SCROLL; 4423 if (hasTopScroll) { 4424 menuClipRect.top = bounds.top + arrowHeight; 4425 if (hitPt.v < menuClipRect.top) { 4426 newItem = -1; 4427 scrollDirection = DOWN_SCROLL; 4428 } 4429 } 4430 if (hasBottomScroll) { 4431 menuClipRect.bottom = bounds.bottom - 1 - arrowHeight; 4432 if (hitPt.v > menuClipRect.bottom) { 4433 newItem = -1; 4434 scrollDirection = UP_SCROLL; 4435 } 4436 } 4437 if (MDEFScrollFlag) { 4438 scrollDirection = DONT_SCROLL; 4439 MDEFScrollFlag = 0; 4440 } 4441 /* 4442 * Don't scroll if there are other menus open above us 4443 */ 4444 ChkErr(GetMenuTrackingData, NULL, &topMtd); 4445 if (menu != topMtd.menu) { 4446 scrollDirection = DONT_SCROLL; 4447 } 4448 if (scrollDirection == DONT_SCROLL) { 4449 /* 4450 * Find out which item was hit. If it is the same as the old item, 4451 * we don't need to do anything. 4452 */ 4453 4454 if (PtInRect(hitPt, menuRectPtr)) { 4455 for (i = 0; i < menuPtr->numEntries; i++) { 4456 mePtr = menuPtr->entries[i]; 4457 itemRect.left = menuRectPtr->left + mePtr->x; 4458 itemRect.top = mtdPtr->virtualMenuTop + mePtr->y; 4459 itemRect.right = mePtr->entryFlags & ENTRY_LAST_COLUMN ? 4460 menuRectPtr->right : itemRect.left + mePtr->width; 4461 itemRect.bottom = itemRect.top + mePtr->height; 4462 if (PtInRect(hitPt, &itemRect)) { 4463 if ((mePtr->type == SEPARATOR_ENTRY) 4464 || (mePtr->state == ENTRY_DISABLED)) { 4465 newItem = -1; 4466 itemUnderMouse = i; 4467 } else { 4468 TkMenuEntry *parentEntryPtr = 4469 GetParentMenuEntry(menuPtr); 4470 4471 if (parentEntryPtr && 4472 parentEntryPtr->state == ENTRY_DISABLED) { 4473 newItem = -1; 4474 itemUnderMouse = i; 4475 } else { 4476 newItem = i; 4477 itemUnderMouse = i; 4478 } 4479 } 4480 break; 4481 } 4482 } 4483 } 4484 } else { 4485 short scrollAmt; 4486 unsigned long scrollDelay; 4487 Rect arrowRect, eraseRect, scrolledMenuClipRect; 4488 ThemeMenuState menuState = IsMenuItemEnabled(menu, 0) ? 4489 kThemeMenuActive : kThemeMenuDisabled; 4490 int oldItem = mtdPtr->itemSelected - 1; 4491 short d; 4492 4493 TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, 4494 0, 0, -1, -1); 4495 scrollAmt = fontMetrics.linespace + menuItemExtraHeight; 4496 if (scrollDirection == UP_SCROLL) { 4497 scrollAmt = -scrollAmt; 4498 d = hitPt.v - bounds.bottom; 4499 } else { 4500 d = bounds.top - hitPt.v; 4501 } 4502 scrollDelay = (d >= scrollAmt/2) ? 1 : 10; 4503 menuClipRect = *menuRectPtr; 4504 if (mtdPtr->virtualMenuTop + scrollAmt < menuRectPtr->top) { 4505 arrowRect = bounds; 4506 /*arrowRect.top += 1;*/ 4507 arrowRect.bottom = arrowRect.top + arrowHeight; 4508 eraseRect = arrowRect; 4509 eraseRect.top = menuRectPtr->top; 4510 menuClipRect.top = arrowRect.bottom; 4511 if (!hasTopScroll) { 4512 ChkErr(EraseMenuBackground, menu, &eraseRect, 4513 macMDEFDrawable.context); 4514 ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, 4515 mtdPtr->virtualMenuTop + scrollAmt, 4516 mtdPtr->virtualMenuBottom + scrollAmt, 4517 menuState, kThemeMenuItemScrollUpArrow, NULL, 0); 4518#ifdef TK_MAC_DEBUG_MENUS 4519 TkMacOSXDbgMsg("upArrow: %d - %d, %d - %d", arrowRect.top, 4520 arrowRect.bottom, arrowRect.left, arrowRect.right); 4521#endif 4522 } 4523 } 4524 if (mtdPtr->virtualMenuBottom + scrollAmt > menuRectPtr->bottom) { 4525 arrowRect = bounds; 4526 arrowRect.bottom -= 1; 4527 arrowRect.top = arrowRect.bottom - arrowHeight; 4528 eraseRect = arrowRect; 4529 eraseRect.bottom = menuRectPtr->bottom; 4530 menuClipRect.bottom = arrowRect.top; 4531 if (!hasBottomScroll) { 4532 ChkErr(EraseMenuBackground, menu, &eraseRect, 4533 macMDEFDrawable.context); 4534 ChkErr(DrawThemeMenuItem, menuRectPtr, &arrowRect, 4535 mtdPtr->virtualMenuTop + scrollAmt, 4536 mtdPtr->virtualMenuBottom + scrollAmt, 4537 menuState, kThemeMenuItemScrollDownArrow, NULL, 0); 4538#ifdef TK_MAC_DEBUG_MENUS 4539 TkMacOSXDbgMsg("downArrow: %d - %d, %d - %d", arrowRect.top, 4540 arrowRect.bottom, arrowRect.left, arrowRect.right); 4541#endif 4542 } 4543 } 4544 TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, 4545 menuClipRect.left, menuClipRect.top, menuClipRect.right - 4546 menuClipRect.left, menuClipRect.bottom - menuClipRect.top); 4547 TkActivateMenuEntry(menuPtr, -1); 4548 if (oldItem >= 0) { 4549 AppearanceEntryDrawWrapper(menuPtr->entries[oldItem], menuRectPtr, 4550 mtdPtr, (Drawable) &macMDEFDrawable, &fontMetrics, 4551 menuFont, 1); 4552 } 4553 ChkErr(ScrollMenuImage, menu, &menuClipRect, 0, scrollAmt, 4554 macMDEFDrawable.context); 4555 mtdPtr->virtualMenuTop += scrollAmt; 4556 mtdPtr->virtualMenuBottom += scrollAmt; 4557 scrolledMenuClipRect = menuClipRect; 4558 OffsetRect(&scrolledMenuClipRect, 0, scrollAmt); 4559 menuClipRect = bounds; 4560 if (mtdPtr->virtualMenuTop < menuRectPtr->top) { 4561 menuClipRect.top += arrowHeight; 4562 } 4563 if (mtdPtr->virtualMenuBottom > menuRectPtr->bottom) { 4564 menuClipRect.bottom -= arrowHeight; 4565 } 4566 TkpClipDrawableToRect(menuPtr->display, (Drawable) &macMDEFDrawable, 4567 menuClipRect.left, menuClipRect.top, menuClipRect.right - 4568 menuClipRect.left, menuClipRect.bottom - menuClipRect.top); 4569 if (scrolledMenuClipRect.bottom < menuClipRect.bottom) { 4570 menuClipRect.top = scrolledMenuClipRect.bottom; 4571 } else if (scrolledMenuClipRect.top < menuClipRect.top) { 4572 menuClipRect.bottom = scrolledMenuClipRect.top; 4573 } 4574 for (i = 0; i < menuPtr->numEntries; i++) { 4575 mePtr = menuPtr->entries[i]; 4576 if (mtdPtr->virtualMenuTop + mePtr->y + mePtr->height < 4577 menuClipRect.top || mtdPtr->virtualMenuTop + mePtr->y > 4578 menuClipRect.bottom) { 4579 continue; 4580 } 4581#ifdef TK_MAC_DEBUG_MENUS 4582 TkMacOSXDbgMsg("Drawing item %i", i); 4583#endif 4584 AppearanceEntryDrawWrapper(mePtr, menuRectPtr, mtdPtr, 4585 (Drawable) &macMDEFDrawable, &fontMetrics, menuFont, 1); 4586 } 4587 Delay(scrollDelay, NULL); 4588 } 4589 mtdPtr->itemSelected = newItem + 1; 4590 mtdPtr->itemUnderMouse = itemUnderMouse + 1; 4591 mtdPtr->itemRect = itemRect; 4592} 4593 4594/* 4595 *---------------------------------------------------------------------- 4596 * 4597 * HandleMenuPopUpMsg -- 4598 * 4599 * Handles the MenuDefProc's PopUp message. The menu is 4600 * posted with the selected item at the point given in hitPt. 4601 * 4602 * Results: 4603 * A menu is posted. 4604 * 4605 * Side effects: 4606 * None. 4607 * 4608 *---------------------------------------------------------------------- 4609 */ 4610void 4611HandleMenuPopUpMsg( 4612 MenuRef menu, 4613 Rect *menuRectPtr, 4614 Point hitPt, 4615 SInt16 *whichItem, 4616 TkMenu *menuPtr) 4617{ 4618 int maxMenuHeight; 4619 int oldItem; 4620 Rect portRect; 4621 BitMap screenBits; 4622 static SInt16 menuBarHeight = 0; 4623 4624#ifdef TK_MAC_DEBUG_MENUS 4625 TkMacOSXDbgMsg("MDEF: PopUpMsg"); 4626#endif 4627 4628 if (!menuBarHeight) { 4629 ChkErr(GetThemeMenuBarHeight, &menuBarHeight); 4630 } 4631 GetQDGlobalsScreenBits(&screenBits); 4632 4633 /* 4634 * Note that for some oddball reason, h and v are reversed in the 4635 * point given to us by the MDEF. 4636 */ 4637 4638 oldItem = *whichItem; 4639 if (oldItem >= menuPtr->numEntries) { 4640 oldItem = -1; 4641 } 4642 portRect.top = 0; 4643 portRect.bottom = 1280; 4644 maxMenuHeight = screenBits.bounds.bottom - screenBits.bounds.top 4645 - menuBarHeight - SCREEN_MARGIN; 4646 if (menuPtr->totalHeight > maxMenuHeight) { 4647 menuRectPtr->top = menuBarHeight; 4648 } else { 4649 int delta; 4650 4651 menuRectPtr->top = hitPt.h; 4652 if (oldItem >= 0) { 4653 menuRectPtr->top -= menuPtr->entries[oldItem]->y; 4654 } 4655 4656 if (menuRectPtr->top < menuBarHeight) { 4657 /* 4658 * Displace downward if the menu would stick off the top of the 4659 * screen. 4660 */ 4661 4662 menuRectPtr->top = menuBarHeight + SCREEN_MARGIN; 4663 } else { 4664 /* 4665 * Or upward if the menu sticks off the bottom end... 4666 */ 4667 4668 delta = menuRectPtr->top + menuPtr->totalHeight - maxMenuHeight; 4669 if (delta > 0) { 4670 menuRectPtr->top -= delta; 4671 } 4672 } 4673 } 4674 menuRectPtr->left = hitPt.v; 4675 menuRectPtr->right = menuRectPtr->left + menuPtr->totalWidth; 4676 menuRectPtr->bottom = menuRectPtr->top + 4677 ((maxMenuHeight < menuPtr->totalHeight) 4678 ? maxMenuHeight : menuPtr->totalHeight); 4679 if (menuRectPtr->top == menuBarHeight) { 4680 *whichItem = hitPt.h; 4681 } else { 4682 *whichItem = menuRectPtr->top; 4683 } 4684} 4685 4686/* 4687 *---------------------------------------------------------------------- 4688 * 4689 * HandleMenuCalcItemMsg -- 4690 * 4691 * Handles the MenuDefProc's CalcItem message. It is supposed 4692 * to calculate the Rect of the menu entry in whichItem in the 4693 * menu, and put that in menuRectPtr. I assume this works, but I 4694 * have never seen the MenuManager send this message. 4695 * 4696 * Results: 4697 * The Menu Manager is informed of the bounding rect of a 4698 * menu rect. 4699 * 4700 * Side effects: 4701 * None. 4702 * 4703 *---------------------------------------------------------------------- 4704 */ 4705 4706void 4707HandleMenuCalcItemMsg( 4708 MenuRef menu, 4709 Rect *menuRectPtr, 4710 Point hitPt, 4711 SInt16 *whichItem, 4712 TkMenu *menuPtr) 4713{ 4714 TkMenuEntry *mePtr; 4715 MenuTrackingData mtd, *mtdPtr = &mtd; 4716 OSStatus err; 4717 int virtualTop, item = *whichItem-1; 4718 4719 err = ChkErr(GetMenuTrackingData, menu, mtdPtr); 4720 if (err == noErr) { 4721 virtualTop = mtdPtr->virtualMenuTop; 4722 } else { 4723 virtualTop = 0; 4724 } 4725 4726 if (item >= 0 && item < menuPtr->numEntries) { 4727 mePtr = menuPtr->entries[item]; 4728 menuRectPtr->left = mePtr->x; 4729 menuRectPtr->top = mePtr->y + virtualTop; 4730 if (mePtr->entryFlags & ENTRY_LAST_COLUMN) { 4731 menuRectPtr->right = menuPtr->totalWidth; 4732 } else { 4733 menuRectPtr->right = mePtr->x + mePtr->width; 4734 } 4735 menuRectPtr->bottom = menuRectPtr->top + mePtr->height; 4736 } 4737#ifdef TK_MAC_DEBUG_MENUS 4738 TkMacOSXDbgMsg("MDEF: CalcItemMsg %d: %d, %d", *whichItem, 4739 menuRectPtr->left, menuRectPtr->top); 4740#endif 4741} 4742#endif /* USE_TK_MDEF */ 4743