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-2009, Apple Inc. 8 * Copyright (c) 2005-2009 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$ 14 */ 15 16#include "tkMacOSXPrivate.h" 17#include "tkMenubutton.h" 18#include "tkMenu.h" 19#include "tkColor.h" 20#include "tkFont.h" 21#include "tkMacOSXWm.h" 22#include "tkMacOSXDebug.h" 23 24/* 25#ifdef TK_MAC_DEBUG 26#define TK_MAC_DEBUG_MENUS 27#endif 28*/ 29 30#define ENTRY_HELP_MENU ENTRY_PLATFORM_FLAG1 31#define ENTRY_APPLE_MENU ENTRY_PLATFORM_FLAG2 32#define ENTRY_WINDOWS_MENU ENTRY_PLATFORM_FLAG3 33 34#define sl(s) ((int) (sizeof(s "") - 1)) 35#define SPECIALMENU(n, f) {.name = "." #n, .len = sl(#n) + 1, \ 36 .flag = ENTRY_##f##_MENU } 37static const struct { 38 const char *name; const size_t len; const int flag; 39} specialMenus[] = { 40 SPECIALMENU(help, HELP), 41 SPECIALMENU(apple, APPLE), 42 SPECIALMENU(window, WINDOWS), 43 {NULL} 44}; 45#undef SPECIALMENU 46#define MODIFIER(n, f) {.name = #n, .len = sl(#n), .mask = f } 47static const struct { 48 const char *name; const size_t len; const NSUInteger mask; 49} modifiers[] = { 50 MODIFIER(Control, NSControlKeyMask), 51 MODIFIER(Ctrl, NSControlKeyMask), 52 MODIFIER(Option, NSAlternateKeyMask), 53 MODIFIER(Opt, NSAlternateKeyMask), 54 MODIFIER(Alt, NSAlternateKeyMask), 55 MODIFIER(Shift, NSShiftKeyMask), 56 MODIFIER(Command, NSCommandKeyMask), 57 MODIFIER(Cmd, NSCommandKeyMask), 58 MODIFIER(Meta, NSCommandKeyMask), 59 {NULL} 60}; 61#undef MODIFIER 62#define ACCEL(n, c) {.name = #n, .len = sl(#n), .ch = c } 63static const struct { 64 const char *name; const size_t len; const UniChar ch; 65} specialAccelerators[] = { 66 ACCEL(PageUp, NSPageUpFunctionKey), 67 ACCEL(PageDown, NSPageDownFunctionKey), 68 ACCEL(Left, NSLeftArrowFunctionKey), 69 ACCEL(Right, NSRightArrowFunctionKey), 70 ACCEL(Up, NSUpArrowFunctionKey), 71 ACCEL(Down, NSDownArrowFunctionKey), 72 ACCEL(Escape, 0x001b), 73 ACCEL(Clear, NSClearDisplayFunctionKey), 74 ACCEL(Enter, NSEnterCharacter), 75 ACCEL(Backspace, NSBackspaceCharacter), 76 ACCEL(Space, ' '), 77 ACCEL(Tab, NSTabCharacter), 78 ACCEL(BackTab, NSBackTabCharacter), 79 ACCEL(Delete, NSDeleteCharacter), 80 ACCEL(Home, NSHomeFunctionKey), 81 ACCEL(End, NSEndFunctionKey), 82 ACCEL(Return, NSCarriageReturnCharacter), 83 ACCEL(Help, NSHelpFunctionKey), 84 ACCEL(Power, 0x233d), 85 ACCEL(Eject, 0xf804), 86 {NULL} 87}; 88#undef ACCEL 89#undef sl 90 91static int gNoTkMenus = 0; /* This is used by Tk_MacOSXTurnOffMenus as 92 * the flag that Tk is not to draw any 93 * menus. */ 94static int inPostMenu = 0; 95static unsigned long defaultBg = 0, defaultFg = 0; 96static SInt32 menuMarkColumnWidth = 0, menuIconTrailingEdgeMargin = 0; 97static SInt32 menuTextLeadingEdgeMargin = 0, menuTextTrailingEdgeMargin = 0; 98static SInt16 menuItemExtraHeight = 0, menuItemExtraWidth = 0; 99static SInt16 menuSeparatorHeight = 0; 100 101static void CheckForSpecialMenu(TkMenu *menuPtr); 102static NSString *ParseAccelerator(const char *accel, NSUInteger *maskPtr); 103static int GenerateMenuSelectEvent(TKMenu *menu, NSMenuItem *menuItem); 104static void MenuSelectEvent(TkMenu *menuPtr); 105static void RecursivelyClearActiveMenu(TkMenu *menuPtr); 106static int ModifierCharWidth(Tk_Font tkfont); 107 108#pragma mark TKMenu 109 110@interface TKMenu(TKMenuPrivate) 111- (id)initWithTkMenu:(TkMenu *)tkMenu; 112- (TkMenu *)tkMenu; 113- (int)tkIndexOfItem:(NSMenuItem *)menuItem; 114- (void)insertItem:(NSMenuItem *)newItem atTkIndex:(NSInteger)index; 115@end 116 117#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 118#define TKMenu_NSMenuDelegate <NSMenuDelegate> 119#else 120#define TKMenu_NSMenuDelegate 121#endif 122@interface TKMenu(TKMenuDelegate) TKMenu_NSMenuDelegate 123@end 124 125@implementation TKMenu 126- (void)setSpecial:(NSUInteger)special { 127 NSAssert(!_tkSpecial, @"Cannot change specialness of a special menu"); 128 _tkSpecial = special; 129} 130- (BOOL)isSpecial:(NSUInteger)special { 131 return (_tkSpecial == special); 132} 133@end 134 135@implementation TKMenu(TKMenuPrivate) 136- (id)initWithTitle:(NSString *)aTitle { 137 self = [super initWithTitle:aTitle]; 138 if (self) { 139 _tkMenu = NULL; 140 _tkOffset = 0; 141 _tkItemCount = 0; 142 _tkSpecial = 0; 143 [self setDelegate:self]; 144 } 145 return self; 146} 147- (id)initWithTkMenu:(TkMenu *)tkMenu { 148 NSString *title = [[NSString alloc] initWithUTF8String: 149 Tk_PathName(tkMenu->tkwin)]; 150 self = [self initWithTitle:title]; 151 [title release]; 152 if (self) { 153 _tkMenu = tkMenu; 154 } 155 return self; 156} 157- (id)copyWithZone:(NSZone *)zone { 158 TKMenu *copy = [super copyWithZone:zone]; 159 NSAssert(_tkMenu == nil, @"Cannot copy tkMenu"); 160 copy->_tkMenu = _tkMenu; 161 copy->_tkOffset = _tkOffset; 162 copy->_tkItemCount = _tkItemCount; 163 copy->_tkSpecial = _tkSpecial; 164 return copy; 165} 166- (TkMenu *)tkMenu { 167 return _tkMenu; 168} 169- (int)tkIndexOfItem:(NSMenuItem *)menuItem { 170 return [self indexOfItem:menuItem] - _tkOffset; 171} 172- (void)insertItem:(NSMenuItem *)newItem atTkIndex:(NSInteger)index { 173 [super insertItem:newItem atIndex:index + _tkOffset]; 174 _tkItemCount++; 175} 176- (void)insertItem:(NSMenuItem *)newItem atIndex:(NSInteger)index { 177 if (_tkMenu && index >= 0) { 178 if ((NSUInteger)index <= _tkOffset) { 179 _tkOffset++; 180 } else { 181 NSAssert((NSUInteger)index >= _tkItemCount + _tkOffset, 182 @"Cannot insert in the middle of Tk menu"); 183 } 184 } 185 [super insertItem:newItem atIndex:index]; 186} 187- (void)removeItemAtIndex:(NSInteger)index { 188 if (_tkMenu && index >= 0) { 189 if ((NSUInteger)index < _tkOffset) { 190 _tkOffset--; 191 } else if ((NSUInteger)index < _tkItemCount + _tkOffset) { 192 _tkItemCount--; 193 } 194 } 195 [super removeItemAtIndex:index]; 196} 197- (NSMenuItem *)newTkMenuItem:(TkMenuEntry *)mePtr { 198 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:@"" 199 action:@selector(tkMenuItemInvoke:) keyEquivalent:@""]; 200 [menuItem setTarget:self]; 201 [menuItem setTag:(NSInteger)mePtr]; 202 return menuItem; 203} 204@end 205 206@implementation TKMenu(TKMenuActions) 207// target methods 208- (BOOL)validateMenuItem:(NSMenuItem *)menuItem { 209 return [menuItem isEnabled]; 210} 211- (void)tkMenuItemInvoke:(id)sender { 212 /* 213 * With the delegate matching key equivalents, when a menu action is sent 214 * in response to a key equivalent, sender is the whole menu and not the 215 * the specific menu item, use this to ignore key equivalents for our menus 216 * (as Tk handles them directly via bindings). 217 */ 218 if ([sender isKindOfClass:[NSMenuItem class]]) { 219 NSMenuItem *menuItem = (NSMenuItem *)sender; 220 TkMenu *menuPtr = (TkMenu *)_tkMenu; 221 TkMenuEntry *mePtr = (TkMenuEntry *)[menuItem tag]; 222 if (menuPtr && mePtr) { 223 Tcl_Interp *interp = menuPtr->interp; 224 Tcl_Preserve(interp); 225 Tcl_Preserve(menuPtr); 226 int result = TkInvokeMenu(interp, menuPtr, mePtr->index); 227 if (result != TCL_OK && result != TCL_CONTINUE && 228 result != TCL_BREAK) { 229 Tcl_AddErrorInfo(interp, "\n (menu invoke)"); 230 Tcl_BackgroundError(interp); 231 } 232 Tcl_Release(menuPtr); 233 Tcl_Release(interp); 234 } 235 } 236} 237@end 238 239@implementation TKMenu(TKMenuDelegate) 240#define keyEquivModifiersMatch(km, m) (( \ 241 ((km) & NSCommandKeyMask) != ((m) & NSCommandKeyMask) || \ 242 ((km) & NSAlternateKeyMask) != ((m) & NSAlternateKeyMask) || \ 243 ((km) & NSControlKeyMask) != ((m) & NSControlKeyMask) || \ 244 (((km) & NSShiftKeyMask) != ((m) & NSShiftKeyMask) && \ 245 ((m) & NSFunctionKeyMask))) ? NO : YES) 246- (BOOL)menuHasKeyEquivalent:(NSMenu*)menu forEvent:(NSEvent*)event 247 target:(id*)target action:(SEL*)action { 248 NSString *key = [event charactersIgnoringModifiers]; 249 NSUInteger modifiers = [event modifierFlags] & 250 NSDeviceIndependentModifierFlagsMask; 251 if (modifiers == (NSCommandKeyMask | NSShiftKeyMask) && 252 [key compare:@"?"] == NSOrderedSame) { 253 return NO; 254 } 255 NSArray *itemArray = [self itemArray]; 256 for (NSMenuItem *item in itemArray) { 257 if ([item isEnabled] && [[item keyEquivalent] compare:key] == 258 NSOrderedSame) { 259 NSUInteger keyEquivModifiers = [item keyEquivalentModifierMask]; 260 if (keyEquivModifiersMatch(keyEquivModifiers, modifiers)) { 261 *target = [item target]; 262 *action = [item action]; 263 return YES; 264 } 265 } 266 } 267 return NO; 268} 269- (void)menuWillOpen:(NSMenu *)menu { 270 if (_tkMenu) { 271 //RecursivelyClearActiveMenu(_tkMenu); 272 GenerateMenuSelectEvent((TKMenu *)[self supermenu], 273 [self itemInSupermenu]); 274 } 275} 276- (void)menuDidClose:(NSMenu *)menu { 277 if (_tkMenu) { 278 RecursivelyClearActiveMenu(_tkMenu); 279 } 280} 281- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item { 282 if (_tkMenu) { 283 GenerateMenuSelectEvent(self, item); 284 } 285} 286- (void)menuNeedsUpdate:(NSMenu*)menu { 287 TkMenu *menuPtr = (TkMenu *)_tkMenu; 288 if (menuPtr) { 289 Tcl_Interp *interp = menuPtr->interp; 290 Tcl_Preserve(interp); 291 Tcl_Preserve(menuPtr); 292 int result = TkPostCommand(_tkMenu); 293 if (result != TCL_OK && result != TCL_CONTINUE && result != TCL_BREAK) { 294 Tcl_AddErrorInfo(interp, "\n (menu preprocess)"); 295 Tcl_BackgroundError(interp); 296 } 297 Tcl_Release(menuPtr); 298 Tcl_Release(interp); 299 } 300} 301@end 302 303#pragma mark TKApplication(TKMenu) 304 305@interface NSApplication(TKMenu) 306- (void)setAppleMenu:(NSMenu *)menu; 307@end 308 309@implementation TKApplication(TKMenu) 310- (void)menuBeginTracking:(NSNotification *)notification { 311#ifdef TK_MAC_DEBUG_NOTIFICATIONS 312 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 313#endif 314 //TkMacOSXClearMenubarActive(); 315 //TkMacOSXPreprocessMenu(); 316} 317- (void)menuEndTracking:(NSNotification *)notification { 318#ifdef TK_MAC_DEBUG_NOTIFICATIONS 319 TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification); 320#endif 321 if (!inPostMenu) { 322 TkMacOSXClearMenubarActive(); 323 } 324} 325- (void)tkSetMainMenu:(TKMenu *)menu { 326 if (gNoTkMenus) { 327 return; 328 } 329 TKMenu *applicationMenu = nil; 330 if (menu) { 331 NSMenuItem *applicationMenuItem = [menu numberOfItems] ? 332 [menu itemAtIndex:0] : nil; 333 if (![menu isSpecial:tkMainMenu]) { 334 TkMenuEntry *mePtr = (TkMenuEntry *)[applicationMenuItem tag]; 335 if (!mePtr || !(mePtr->entryFlags & ENTRY_APPLE_MENU)) { 336 applicationMenuItem = [NSMenuItem itemWithSubmenu: 337 [[_defaultApplicationMenu copy] autorelease]]; 338 [menu insertItem:applicationMenuItem atIndex:0]; 339 } 340 [menu setSpecial:tkMainMenu]; 341 } 342 applicationMenu = (TKMenu *)[applicationMenuItem submenu]; 343 if (![applicationMenu isSpecial:tkApplicationMenu]) { 344 for (NSMenuItem *item in _defaultApplicationMenuItems) { 345 [applicationMenu addItem:[[item copy] autorelease]]; 346 } 347 [applicationMenu setSpecial:tkApplicationMenu]; 348 } 349 NSArray *itemArray = [menu itemArray]; 350 for (NSMenuItem *item in itemArray) { 351 TkMenuEntry *mePtr = (TkMenuEntry *)[item tag]; 352 TKMenu *submenu = (TKMenu *)[item submenu]; 353 if (mePtr && submenu) { 354 if ((mePtr->entryFlags & ENTRY_WINDOWS_MENU) && 355 ![submenu isSpecial:tkWindowsMenu]) { 356 NSInteger index = 0; 357 for (NSMenuItem *i in _defaultWindowsMenuItems) { 358 [submenu insertItem:[[i copy] autorelease] atIndex: 359 index++]; 360 } 361 [self setWindowsMenu:submenu]; 362 [submenu setSpecial:tkWindowsMenu]; 363 } else if ((mePtr->entryFlags & ENTRY_HELP_MENU) && 364 ![submenu isSpecial:tkHelpMenu]) { 365 NSInteger index = 0; 366 for (NSMenuItem *i in _defaultHelpMenuItems) { 367 [submenu insertItem:[[i copy] autorelease] atIndex: 368 index++]; 369 } 370 [submenu setSpecial:tkHelpMenu]; 371 } 372 } 373 } 374 } else { 375 menu = _defaultMainMenu; 376 applicationMenu = _defaultApplicationMenu; 377 } 378 NSMenuItem *servicesMenuItem = [applicationMenu itemWithTitle:@"Services"]; 379 if (servicesMenuItem && [servicesMenuItem submenu] != _servicesMenu) { 380 [[_servicesMenu itemInSupermenu] setSubmenu:nil]; 381 [servicesMenuItem setSubmenu:_servicesMenu]; 382 } 383 [self setAppleMenu:applicationMenu]; 384 [self setMainMenu:menu]; 385} 386@end 387 388#pragma mark - 389 390/* 391 *---------------------------------------------------------------------- 392 * 393 * TkpNewMenu -- 394 * 395 * Gets a new blank menu. Only the platform specific options are filled 396 * in. 397 * 398 * Results: 399 * Returns a standard Tcl error. 400 * 401 * Side effects: 402 * Allocates a NSMenu and puts it into the platformData field 403 * of the menuPtr. 404 * 405 *---------------------------------------------------------------------- 406 */ 407 408int 409TkpNewMenu( 410 TkMenu *menuPtr) /* The common structure we are making the 411 * platform structure for. */ 412{ 413 TKMenu *menu = [[TKMenu alloc] initWithTkMenu:menuPtr]; 414 menuPtr->platformData = (TkMenuPlatformData) 415 TkMacOSXMakeUncollectable(menu); 416 CheckForSpecialMenu(menuPtr); 417 return TCL_OK; 418} 419 420/* 421 *---------------------------------------------------------------------- 422 * 423 * TkpDestroyMenu -- 424 * 425 * Destroys platform-specific menu structures. 426 * 427 * Results: 428 * None. 429 * 430 * Side effects: 431 * All platform-specific allocations are freed up. 432 * 433 *---------------------------------------------------------------------- 434 */ 435 436void 437TkpDestroyMenu( 438 TkMenu *menuPtr) /* The common menu structure */ 439{ 440 TkMacOSXMakeCollectableAndRelease(menuPtr->platformData); 441} 442 443/* 444 *---------------------------------------------------------------------- 445 * 446 * TkpMenuNewEntry -- 447 * 448 * Adds a pointer to a new menu entry structure with the platform- 449 * specific fields filled in. The Macintosh uses the platformEntryData 450 * field of the TkMenuEntry record. 451 * 452 * Results: 453 * Standard TCL error. 454 * 455 * Side effects: 456 * Storage gets allocated. New menu entry data is put into the 457 * platformEntryData field of the mePtr. 458 * 459 *---------------------------------------------------------------------- 460 */ 461 462int 463TkpMenuNewEntry( 464 TkMenuEntry *mePtr) /* The menu we are adding an entry to */ 465{ 466 TKMenu *menu = (TKMenu *) mePtr->menuPtr->platformData; 467 NSMenuItem *menuItem; 468 if (mePtr->type == SEPARATOR_ENTRY || mePtr->type == TEAROFF_ENTRY) { 469 menuItem = [[NSMenuItem separatorItem] retain]; 470 } else { 471 menuItem = [menu newTkMenuItem:mePtr]; 472 } 473 mePtr->platformEntryData = (TkMenuPlatformEntryData) 474 TkMacOSXMakeUncollectable(menuItem); 475 /* Caller TkMenuEntry() already did this same insertion into the generic 476 * TkMenu so we just match it for the platform menu. */ 477 [menu insertItem:menuItem atTkIndex:mePtr->index]; 478 return TCL_OK; 479} 480 481/* 482 *---------------------------------------------------------------------- 483 * 484 * TkpConfigureMenuEntry -- 485 * 486 * Processes configurations for menu entries. 487 * 488 * Results: 489 * Returns standard TCL result. If TCL_ERROR is returned, then the 490 * interp's result contains an error message. 491 * 492 * Side effects: 493 * Configuration information get set for mePtr; old resources get freed, 494 * if any need it. 495 * 496 *---------------------------------------------------------------------- 497 */ 498 499int 500TkpConfigureMenuEntry( 501 TkMenuEntry *mePtr) /* Information about menu entry; may or may 502 * not already have values for some fields. */ 503{ 504 NSMenuItem *menuItem = (NSMenuItem *) mePtr->platformEntryData; 505 NSString *title = @""; 506 NSAttributedString *attributedTitle = nil; 507 NSImage *image = nil; 508 NSString *keyEquivalent = @""; 509 NSUInteger modifierMask = NSCommandKeyMask; 510 NSMenu *submenu = nil; 511 NSDictionary *attributes; 512 int imageWidth, imageHeight; 513 GC gc = (mePtr->textGC ? mePtr->textGC : mePtr->menuPtr->textGC); 514 Tcl_Obj *fontPtr = (mePtr->fontPtr ? mePtr->fontPtr : 515 mePtr->menuPtr->fontPtr); 516 517 if (mePtr->image) { 518 Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); 519 image = TkMacOSXGetNSImageWithTkImage(mePtr->menuPtr->display, 520 mePtr->image, imageWidth, imageHeight); 521 } else if (mePtr->bitmapPtr != None) { 522 Pixmap bitmap = Tk_GetBitmapFromObj(mePtr->menuPtr->tkwin, 523 mePtr->bitmapPtr); 524 Tk_SizeOfBitmap(mePtr->menuPtr->display, bitmap, &imageWidth, 525 &imageHeight); 526 image = TkMacOSXGetNSImageWithBitmap(mePtr->menuPtr->display, bitmap, 527 gc, imageWidth, imageHeight); 528 } 529 [menuItem setImage:image]; 530 if ((!image || mePtr->compound != COMPOUND_NONE) && mePtr->labelPtr && 531 mePtr->labelLength) { 532 title = [[[NSString alloc] initWithBytes:Tcl_GetString(mePtr->labelPtr) 533 length:mePtr->labelLength encoding:NSUTF8StringEncoding] 534 autorelease]; 535 if ([title hasSuffix:@"..."]) { 536 title = [NSString stringWithFormat:@"%@%C", 537 [title substringToIndex:[title length] - 3], 0x2026]; 538 } 539 } 540 [menuItem setTitle:title]; 541 if (strcmp(Tcl_GetString(fontPtr), "menu") || gc->foreground != defaultFg 542 || gc->background != defaultBg) { 543 attributes = TkMacOSXNSFontAttributesForFont(Tk_GetFontFromObj( 544 mePtr->menuPtr->tkwin, fontPtr)); 545 if (gc->foreground != defaultFg || gc->background != defaultBg) { 546 NSColor *color = TkMacOSXGetNSColor(gc, gc->foreground != defaultFg ? 547 gc->foreground : gc->background); 548 attributes = [[attributes mutableCopy] autorelease]; 549 [(NSMutableDictionary *)attributes setObject:color 550 forKey:NSForegroundColorAttributeName]; 551 } 552 if (attributes) { 553 attributedTitle = [[[NSAttributedString alloc] 554 initWithString:title attributes:attributes] autorelease]; 555 } 556 } 557 [menuItem setAttributedTitle:attributedTitle]; 558 [menuItem setEnabled:!(mePtr->state == ENTRY_DISABLED)]; 559 [menuItem setState:((mePtr->type == CHECK_BUTTON_ENTRY || 560 mePtr->type == RADIO_BUTTON_ENTRY) && mePtr->indicatorOn && 561 (mePtr->entryFlags & ENTRY_SELECTED) ? NSOnState : NSOffState)]; 562 if (mePtr->type != CASCADE_ENTRY && mePtr->accelPtr && mePtr->accelLength) { 563 keyEquivalent = ParseAccelerator(Tcl_GetString(mePtr->accelPtr), 564 &modifierMask); 565 } 566 [menuItem setKeyEquivalent:keyEquivalent]; 567 [menuItem setKeyEquivalentModifierMask:modifierMask]; 568 if (mePtr->type == CASCADE_ENTRY && mePtr->namePtr) { 569 TkMenuReferences *menuRefPtr; 570 571 menuRefPtr = TkFindMenuReferencesObj(mePtr->menuPtr->interp, 572 mePtr->namePtr); 573 if (menuRefPtr && menuRefPtr->menuPtr) { 574 CheckForSpecialMenu(menuRefPtr->menuPtr); 575 submenu = (TKMenu *) menuRefPtr->menuPtr->platformData; 576 if ([submenu supermenu] && [menuItem submenu] != submenu) { 577 /* 578 * This happens during a clone, where the parent menu is cloned 579 * before its children, so just ignore this temprary setting, 580 * it will be changed shortly (c.f. tkMenu.c CloneMenu()) 581 */ 582 submenu = nil; 583 } else { 584 [submenu setTitle:title]; 585 } 586 } 587 } 588 [menuItem setSubmenu:submenu]; 589 return TCL_OK; 590} 591 592/* 593 *---------------------------------------------------------------------- 594 * 595 * TkpDestroyMenuEntry -- 596 * 597 * Cleans up platform-specific menu entry items. 598 * 599 * Results: 600 * None 601 * 602 * Side effects: 603 * All platform-specific allocations are freed up. 604 * 605 *---------------------------------------------------------------------- 606 */ 607 608void 609TkpDestroyMenuEntry( 610 TkMenuEntry *mePtr) 611{ 612 if (mePtr->platformEntryData && mePtr->menuPtr->platformData) { 613 TKMenu *menu = (TKMenu *) mePtr->menuPtr->platformData; 614 NSMenuItem *menuItem = (NSMenuItem *)mePtr->platformEntryData; 615 NSInteger index = [menu indexOfItem:menuItem]; 616 if (index > -1) { 617 [menu removeItemAtIndex:index]; 618 } 619 } 620 TkMacOSXMakeCollectableAndRelease(mePtr->platformEntryData); 621} 622 623/* 624 *---------------------------------------------------------------------- 625 * 626 * TkpPostMenu -- 627 * 628 * Posts a menu on the screen 629 * 630 * Results: 631 * None. 632 * 633 * Side effects: 634 * The menu is posted and handled. 635 * 636 *---------------------------------------------------------------------- 637 */ 638 639int 640TkpPostMenu( 641 Tcl_Interp *interp, /* The interpreter this menu lives in */ 642 TkMenu *menuPtr, /* The menu we are posting */ 643 int x, /* The global x-coordinate of the top, left- 644 * hand corner of where the menu is supposed 645 * to be posted. */ 646 int y) /* The global y-coordinate */ 647{ 648 NSWindow *win = [NSApp keyWindow]; 649 if (win) { 650 inPostMenu = 1; 651 int oldMode = Tcl_SetServiceMode(TCL_SERVICE_NONE); 652 NSView *view = [win contentView]; 653 NSRect frame = NSMakeRect(x + 9, tkMacOSXZeroScreenHeight - y - 9, 1, 1); 654 frame.origin = [view convertPoint: 655 [win convertScreenToBase:frame.origin] fromView:nil]; 656 NSMenu *menu = (NSMenu *) menuPtr->platformData; 657 NSPopUpButtonCell *popUpButtonCell = [[NSPopUpButtonCell alloc] 658 initTextCell:@"" pullsDown:NO]; 659 [popUpButtonCell setMenu:menu]; 660 [popUpButtonCell selectItem:nil]; 661 [popUpButtonCell performClickWithFrame:frame inView:view]; 662 [popUpButtonCell release]; 663 Tcl_SetServiceMode(oldMode); 664 inPostMenu = 0; 665 return TCL_OK; 666 } else { 667 return TCL_ERROR; 668 } 669} 670 671/* 672 *---------------------------------------------------------------------- 673 * 674 * TkpSetWindowMenuBar -- 675 * 676 * Associates a given menu with a window. 677 * 678 * Results: 679 * None. 680 * 681 * Side effects: 682 * On Windows and UNIX, associates the platform menu with the platform 683 * window. 684 * 685 *---------------------------------------------------------------------- 686 */ 687 688void 689TkpSetWindowMenuBar( 690 Tk_Window tkwin, /* The window we are setting the menu in */ 691 TkMenu *menuPtr) /* The menu we are setting */ 692{ 693 TkWindow *winPtr = (TkWindow *) tkwin; 694 695 if (winPtr->wmInfoPtr) { 696 winPtr->wmInfoPtr->menuPtr = menuPtr; 697 } 698} 699 700/* 701 *---------------------------------------------------------------------- 702 * 703 * TkpSetMainMenubar -- 704 * 705 * Puts the menu associated with a window into the menubar. Should only 706 * be called when the window is in front. 707 * 708 * Results: 709 * None. 710 * 711 * Side effects: 712 * The menubar is changed. 713 * 714 *---------------------------------------------------------------------- 715 */ 716 717void 718TkpSetMainMenubar( 719 Tcl_Interp *interp, /* The interpreter of the application */ 720 Tk_Window tkwin, /* The frame we are setting up */ 721 char *menuName) /* The name of the menu to put in front. If 722 * NULL, use the default menu bar. */ 723{ 724 static Tcl_Interp *currentInterp = NULL; 725 TKMenu *menu = nil; 726 if (menuName) { 727 TkWindow *winPtr = (TkWindow *) tkwin; 728 if (winPtr->wmInfoPtr && winPtr->wmInfoPtr->menuPtr && 729 winPtr->wmInfoPtr->menuPtr->masterMenuPtr && 730 winPtr->wmInfoPtr->menuPtr->masterMenuPtr->tkwin && 731 !strcmp(menuName, Tk_PathName( 732 winPtr->wmInfoPtr->menuPtr->masterMenuPtr->tkwin))) { 733 menu = (TKMenu *) winPtr->wmInfoPtr->menuPtr->platformData; 734 } else { 735 TkMenuReferences *menuRefPtr = TkFindMenuReferences(interp, 736 menuName); 737 if (menuRefPtr && menuRefPtr->menuPtr && 738 menuRefPtr->menuPtr->platformData) { 739 menu = (TKMenu *) menuRefPtr->menuPtr->platformData; 740 } 741 } 742 } 743 if (menu || interp != currentInterp) { 744 [NSApp tkSetMainMenu:menu]; 745 } 746 currentInterp = interp; 747} 748 749/* 750 *---------------------------------------------------------------------- 751 * 752 * CheckForSpecialMenu -- 753 * 754 * Given a menu, check to see whether or not it is a cascade in 755 * a menubar with one of the special names .apple, .help or .window 756 * If it is, the entry that points to this menu will be marked. 757 * 758 * Results: 759 * None. 760 * 761 * Side effects: 762 * Will set entryFlags appropriately. 763 * 764 *---------------------------------------------------------------------- 765 */ 766 767static void 768CheckForSpecialMenu( 769 TkMenu *menuPtr) /* The menu we are checking */ 770{ 771 if (!menuPtr->masterMenuPtr->tkwin) { 772 return; 773 } 774 for (TkMenuEntry *cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; 775 cascadeEntryPtr; cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { 776 if (cascadeEntryPtr->menuPtr->menuType == MENUBAR 777 && cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin) { 778 TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr; 779 int i = 0; 780 Tcl_DString ds; 781 782 Tcl_DStringInit(&ds); 783 Tcl_DStringAppend(&ds, Tk_PathName(masterMenuPtr->tkwin), -1); 784 while (specialMenus[i].name) { 785 Tcl_DStringAppend(&ds, specialMenus[i].name, 786 specialMenus[i].len); 787 if (strcmp(Tcl_DStringValue(&ds), 788 Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) { 789 cascadeEntryPtr->entryFlags |= specialMenus[i].flag; 790 } else { 791 cascadeEntryPtr->entryFlags &= ~specialMenus[i].flag; 792 } 793 Tcl_DStringSetLength(&ds, Tcl_DStringLength(&ds) - 794 specialMenus[i].len); 795 i++; 796 } 797 Tcl_DStringFree(&ds); 798 } 799 } 800} 801 802/* 803 *---------------------------------------------------------------------- 804 * 805 * ParseAccelerator -- 806 * 807 * Parse accelerator string. 808 * 809 * Results: 810 * Accelerator string & flags. 811 * 812 * Side effects: 813 * None. 814 * 815 *---------------------------------------------------------------------- 816 */ 817 818static NSString* 819ParseAccelerator( 820 const char *accel, 821 NSUInteger *maskPtr) 822{ 823 unichar ch = 0; 824 size_t len; 825 int i; 826 827 *maskPtr = 0; 828 while (1) { 829 i = 0; 830 while (modifiers[i].name) { 831 int l = modifiers[i].len; 832 833 if (!strncasecmp(accel, modifiers[i].name, l) && 834 (accel[l] == '-' || accel[l] == '+')) { 835 *maskPtr |= modifiers[i].mask; 836 accel += l+1; 837 break; 838 } 839 i++; 840 } 841 if (!modifiers[i].name || !*accel) { 842 break; 843 } 844 } 845 len = strlen(accel); 846 if (len > 1) { 847 i = 0; 848 if (accel[0] == 'F' && len < 4 && accel[1] > '0' && accel[1] <= '9') { 849 int fkey = accel[1] - '0'; 850 851 if (len == 3) { 852 if (accel[2] >= '0' && accel[2] <= '9') { 853 fkey = 10 * fkey + (accel[2] - '0'); 854 } else { 855 fkey = 0; 856 } 857 } 858 if (fkey >= 1 && fkey <= 15) { 859 ch = NSF1FunctionKey + fkey - 1; 860 } 861 } else while (specialAccelerators[i].name) { 862 if (accel[0] == specialAccelerators[i].name[0] && 863 len == specialAccelerators[i].len && !strncasecmp(accel, 864 specialAccelerators[i].name, specialAccelerators[i].len)) { 865 ch = specialAccelerators[i].ch; 866 break; 867 } 868 i++; 869 } 870 } 871 if (ch) { 872 return [[[NSString alloc] initWithCharacters:&ch length:1] autorelease]; 873 } else { 874 return [[[[NSString alloc] initWithUTF8String:accel] autorelease] 875 lowercaseString]; 876 } 877} 878 879/* 880 *-------------------------------------------------------------- 881 * 882 * ModifierCharWidth -- 883 * 884 * Helper mesuring width of command char in given font. 885 * 886 * Results: 887 * Width of command char. 888 * 889 * Side effects: 890 * None. 891 * 892 *-------------------------------------------------------------- 893 */ 894 895static int 896ModifierCharWidth(Tk_Font tkfont) 897{ 898 static NSString *cmdChar = nil; 899 900 if (!cmdChar) { 901 unichar cmd = kCommandUnicode; 902 cmdChar = [[NSString alloc] initWithCharacters:&cmd length:1]; 903 } 904 return [cmdChar sizeWithAttributes: 905 TkMacOSXNSFontAttributesForFont(tkfont)].width; 906} 907 908/* 909 *-------------------------------------------------------------- 910 * 911 * TkpComputeStandardMenuGeometry -- 912 * 913 * This procedure is invoked to recompute the size and layout of a menu 914 * that is not a menubar clone. 915 * 916 * Results: 917 * None. 918 * 919 * Side effects: 920 * Fields of menu entries are changed to reflect their current positions, 921 * and the size of the menu window itself may be changed. 922 * 923 *-------------------------------------------------------------- 924 */ 925 926void 927TkpComputeStandardMenuGeometry( 928 TkMenu *menuPtr) /* Structure describing menu. */ 929{ 930 Tk_Font tkfont, menuFont; 931 Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; 932 int modifierCharWidth, menuModifierCharWidth; 933 int x, y, modifierWidth, labelWidth, indicatorSpace; 934 int windowWidth, windowHeight, accelWidth; 935 int i, j, lastColumnBreak, maxWidth; 936 int entryWidth, maxIndicatorSpace, borderWidth, activeBorderWidth; 937 TkMenuEntry *mePtr, *columnEntryPtr; 938 int haveAccel = 0; 939 940 if (menuPtr->tkwin == NULL) { 941 return; 942 } 943 944 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr, 945 &borderWidth); 946 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr, 947 &activeBorderWidth); 948 x = y = borderWidth; 949 windowHeight = maxWidth = lastColumnBreak = 0; 950 maxIndicatorSpace = 0; 951 952 /* 953 * On the Mac especially, getting font metrics can be quite slow, so we 954 * want to do it intelligently. We are going to precalculate them and pass 955 * them down to all of the measuring and drawing routines. We will measure 956 * the font metrics of the menu once. If an entry does not have its own 957 * font set, then we give the geometry/drawing routines the menu's font 958 * and metrics. If an entry has its own font, we will measure that font 959 * and give all of the geometry/drawing the entry's font and metrics. 960 */ 961 962 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); 963 Tk_GetFontMetrics(menuFont, &menuMetrics); 964 menuModifierCharWidth = ModifierCharWidth(menuFont); 965 966 for (i = 0; i < menuPtr->numEntries; i++) { 967 mePtr = menuPtr->entries[i]; 968 if (mePtr->type == CASCADE_ENTRY || mePtr->accelLength > 0) { 969 haveAccel = 1; 970 break; 971 } 972 } 973 974 for (i = 0; i < menuPtr->numEntries; i++) { 975 mePtr = menuPtr->entries[i]; 976 if (mePtr->fontPtr == NULL) { 977 tkfont = menuFont; 978 fmPtr = &menuMetrics; 979 modifierCharWidth = menuModifierCharWidth; 980 } else { 981 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); 982 Tk_GetFontMetrics(tkfont, &entryMetrics); 983 fmPtr = &entryMetrics; 984 modifierCharWidth = ModifierCharWidth(tkfont); 985 } 986 987 if ((i > 0) && mePtr->columnBreak) { 988 if (maxIndicatorSpace != 0) { 989 maxIndicatorSpace += 2; 990 } 991 for (j = lastColumnBreak; j < i; j++) { 992 columnEntryPtr = menuPtr->entries[j]; 993 columnEntryPtr->indicatorSpace = maxIndicatorSpace; 994 columnEntryPtr->width = maxIndicatorSpace + maxWidth 995 + 2 * activeBorderWidth; 996 columnEntryPtr->x = x; 997 columnEntryPtr->entryFlags &= ~ENTRY_LAST_COLUMN; 998 } 999 x += maxIndicatorSpace + maxWidth + 2 * borderWidth; 1000 maxWidth = maxIndicatorSpace = 0; 1001 lastColumnBreak = i; 1002 y = borderWidth; 1003 } 1004 accelWidth = modifierWidth = indicatorSpace = 0; 1005 if (mePtr->type == SEPARATOR_ENTRY || mePtr->type == TEAROFF_ENTRY) { 1006 mePtr->height = menuSeparatorHeight; 1007 } else { 1008 /* 1009 * For each entry, compute the height required by that particular 1010 * entry, plus three widths: the width of the label, the width to 1011 * allow for an indicator to be displayed to the left of the label 1012 * (if any), and the width of the accelerator to be displayed to 1013 * the right of the label (if any). These sizes depend, of course, 1014 * on the type of the entry. 1015 */ 1016 1017 NSMenuItem *menuItem = (NSMenuItem *) mePtr->platformEntryData; 1018 int haveImage = 0, width = 0, height = 0; 1019 1020 if (mePtr->image) { 1021 Tk_SizeOfImage(mePtr->image, &width, &height); 1022 haveImage = 1; 1023 } else if (mePtr->bitmapPtr) { 1024 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, 1025 mePtr->bitmapPtr); 1026 Tk_SizeOfBitmap(menuPtr->display, bitmap, &width, &height); 1027 haveImage = 1; 1028 } 1029 if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { 1030 NSAttributedString *attrTitle = [menuItem attributedTitle]; 1031 NSSize size; 1032 1033 if (attrTitle) { 1034 size = [attrTitle size]; 1035 } else { 1036 size = [[menuItem title] sizeWithAttributes: 1037 TkMacOSXNSFontAttributesForFont(tkfont)]; 1038 } 1039 size.width += menuTextLeadingEdgeMargin + 1040 menuTextTrailingEdgeMargin; 1041 if (size.height < fmPtr->linespace) { 1042 size.height = fmPtr->linespace; 1043 } 1044 if (haveImage && (mePtr->compound != COMPOUND_NONE)) { 1045 int margin = width + menuIconTrailingEdgeMargin; 1046 1047 if (margin > menuTextLeadingEdgeMargin) { 1048 margin = menuTextLeadingEdgeMargin; 1049 } 1050 width += size.width + menuIconTrailingEdgeMargin - margin; 1051 if (size.height > height) { 1052 height = size.height; 1053 } 1054 } else { 1055 width = size.width; 1056 height = size.height; 1057 } 1058 } 1059 labelWidth = width + menuItemExtraWidth; 1060 mePtr->height = height + menuItemExtraHeight; 1061 1062 if (mePtr->type == CASCADE_ENTRY) { 1063 modifierWidth = modifierCharWidth; 1064 } else if (mePtr->accelLength == 0) { 1065 if (haveAccel && !mePtr->hideMargin) { 1066 modifierWidth = modifierCharWidth; 1067 } 1068 } else { 1069 NSUInteger modifMask = [menuItem keyEquivalentModifierMask]; 1070 int i = 0; 1071 1072 while (modifiers[i].name) { 1073 if (modifMask & modifiers[i].mask) { 1074 modifMask &= ~modifiers[i].mask; 1075 modifierWidth += modifierCharWidth; 1076 } 1077 i++; 1078 } 1079 accelWidth = [[menuItem keyEquivalent] sizeWithAttributes: 1080 TkMacOSXNSFontAttributesForFont(tkfont)].width; 1081 } 1082 if (!mePtr->hideMargin) { 1083 indicatorSpace = menuMarkColumnWidth; 1084 } 1085 if (indicatorSpace > maxIndicatorSpace) { 1086 maxIndicatorSpace = indicatorSpace; 1087 } 1088 entryWidth = labelWidth + modifierWidth + accelWidth; 1089 if (entryWidth > maxWidth) { 1090 maxWidth = entryWidth; 1091 } 1092 mePtr->height += 2 * activeBorderWidth; 1093 } 1094 mePtr->y = y; 1095 y += menuPtr->entries[i]->height + borderWidth; 1096 if (y > windowHeight) { 1097 windowHeight = y; 1098 } 1099 } 1100 1101 for (j = lastColumnBreak; j < menuPtr->numEntries; j++) { 1102 columnEntryPtr = menuPtr->entries[j]; 1103 columnEntryPtr->indicatorSpace = maxIndicatorSpace; 1104 columnEntryPtr->width = maxIndicatorSpace + maxWidth 1105 + 2 * activeBorderWidth; 1106 columnEntryPtr->x = x; 1107 columnEntryPtr->entryFlags |= ENTRY_LAST_COLUMN; 1108 } 1109 windowWidth = x + maxIndicatorSpace + maxWidth 1110 + 2 * activeBorderWidth + borderWidth; 1111 windowHeight += borderWidth; 1112 1113 if (windowWidth <= 0) { 1114 windowWidth = 1; 1115 } 1116 if (windowHeight <= 0) { 1117 windowHeight = 1; 1118 } 1119 menuPtr->totalWidth = windowWidth; 1120 menuPtr->totalHeight = windowHeight; 1121} 1122 1123/* 1124 *---------------------------------------------------------------------- 1125 * 1126 * GenerateMenuSelectEvent -- 1127 * 1128 * Respond to a menu item being selected. 1129 * 1130 * Results: 1131 * True if event(s) are generated - false otherwise. 1132 * 1133 * Side effects: 1134 * Places a virtual event on the event queue. 1135 * 1136 *---------------------------------------------------------------------- 1137 */ 1138 1139int 1140GenerateMenuSelectEvent( 1141 TKMenu *menu, 1142 NSMenuItem *menuItem) 1143{ 1144 TkMenu *menuPtr = [menu tkMenu]; 1145 1146 if (menuPtr) { 1147 int index = [menu tkIndexOfItem:menuItem]; 1148 if (index < 0 || index >= menuPtr->numEntries || 1149 (menuPtr->entries[index])->state == ENTRY_DISABLED) { 1150 TkActivateMenuEntry(menuPtr, -1); 1151 } else { 1152 TkActivateMenuEntry(menuPtr, index); 1153 MenuSelectEvent(menuPtr); 1154 return true; 1155 } 1156 } 1157 return false; 1158} 1159 1160/* 1161 *---------------------------------------------------------------------- 1162 * 1163 * MenuSelectEvent -- 1164 * 1165 * Generates a "MenuSelect" virtual event. This can be used to do 1166 * context-sensitive menu help. 1167 * 1168 * Results: 1169 * None. 1170 * 1171 * Side effects: 1172 * Places a virtual event on the event queue. 1173 * 1174 *---------------------------------------------------------------------- 1175 */ 1176 1177void 1178MenuSelectEvent( 1179 TkMenu *menuPtr) /* the menu we have selected. */ 1180{ 1181 XVirtualEvent event; 1182 1183 bzero(&event, sizeof(XVirtualEvent)); 1184 event.type = VirtualEvent; 1185 event.serial = LastKnownRequestProcessed(menuPtr->display); 1186 event.send_event = false; 1187 event.display = menuPtr->display; 1188 event.event = Tk_WindowId(menuPtr->tkwin); 1189 event.root = XRootWindow(menuPtr->display, 0); 1190 event.subwindow = None; 1191 event.time = TkpGetMS(); 1192 XQueryPointer(NULL, None, NULL, NULL, &event.x_root, &event.y_root, NULL, 1193 NULL, &event.state); 1194 event.same_screen = true; 1195 event.name = Tk_GetUid("MenuSelect"); 1196 Tk_MakeWindowExist(menuPtr->tkwin); 1197 if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) { 1198 Tk_HandleEvent((XEvent *) &event); 1199 } else { 1200 Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); 1201 } 1202} 1203 1204/* 1205 *---------------------------------------------------------------------- 1206 * 1207 * RecursivelyClearActiveMenu -- 1208 * 1209 * Recursively clears the active entry in the menu's cascade hierarchy. 1210 * 1211 * Results: 1212 * None. 1213 * 1214 * Side effects: 1215 * Generates <<MenuSelect>> virtual events. 1216 * 1217 *---------------------------------------------------------------------- 1218 */ 1219 1220void 1221RecursivelyClearActiveMenu( 1222 TkMenu *menuPtr) /* The menu to reset. */ 1223{ 1224 int i; 1225 TkMenuEntry *mePtr; 1226 1227 TkActivateMenuEntry(menuPtr, -1); 1228 for (i = 0; i < menuPtr->numEntries; i++) { 1229 mePtr = menuPtr->entries[i]; 1230 if (mePtr->type == CASCADE_ENTRY) { 1231 if ((mePtr->childMenuRefPtr != NULL) 1232 && (mePtr->childMenuRefPtr->menuPtr != NULL)) { 1233 RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr); 1234 } 1235 } 1236 } 1237} 1238 1239/* 1240 *---------------------------------------------------------------------- 1241 * 1242 * TkMacOSXClearMenubarActive -- 1243 * 1244 * Recursively clears the active entry in the current menubar hierarchy. 1245 * 1246 * Results: 1247 * None. 1248 * 1249 * Side effects: 1250 * Generates <<MenuSelect>> virtual events. 1251 * 1252 *---------------------------------------------------------------------- 1253 */ 1254 1255void 1256TkMacOSXClearMenubarActive(void) 1257{ 1258 NSMenu *mainMenu = [NSApp mainMenu]; 1259 if (mainMenu && [mainMenu isKindOfClass:[TKMenu class]]) { 1260 TkMenu *menuPtr = [(TKMenu *)mainMenu tkMenu]; 1261 if (menuPtr && menuPtr->numEntries && menuPtr->entries) { 1262 RecursivelyClearActiveMenu(menuPtr); 1263 } 1264 } 1265} 1266 1267/* 1268 *---------------------------------------------------------------------- 1269 * 1270 * Tk_MacOSXTurnOffMenus -- 1271 * 1272 * Turns off all the menu drawing code. This is more than just disabling 1273 * the "menu" command, this means that Tk will NEVER touch the menubar. 1274 * It is needed in the Plugin, where Tk does not own the menubar. 1275 * 1276 * Results: 1277 * None. 1278 * 1279 * Side effects: 1280 * A flag is set which will disable all menu drawing. 1281 * 1282 *---------------------------------------------------------------------- 1283 */ 1284 1285void 1286Tk_MacOSXTurnOffMenus(void) 1287{ 1288 gNoTkMenus = 1; 1289} 1290 1291/* 1292 *---------------------------------------------------------------------- 1293 * 1294 * TkpMenuInit -- 1295 * 1296 * Initializes Mac-specific menu data. 1297 * 1298 * Results: 1299 * None. 1300 * 1301 * Side effects: 1302 * Allocates a hash table. 1303 * 1304 *---------------------------------------------------------------------- 1305 */ 1306 1307void 1308TkpMenuInit(void) 1309{ 1310 TkColor *tkColPtr; 1311 1312 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 1313#define observe(n, s) [nc addObserver:NSApp selector:@selector(s) name:(n) object:nil] 1314 observe(NSMenuDidBeginTrackingNotification, menuBeginTracking:); 1315 observe(NSMenuDidEndTrackingNotification, menuEndTracking:); 1316#undef observe 1317 1318 [NSMenuItem setUsesUserKeyEquivalents:NO]; 1319 tkColPtr = TkpGetColor(None, DEF_MENU_BG_COLOR); 1320 defaultBg = tkColPtr->color.pixel; 1321 ckfree((char *) tkColPtr); 1322 tkColPtr = TkpGetColor(None, DEF_MENU_FG); 1323 defaultFg = tkColPtr->color.pixel; 1324 ckfree((char *) tkColPtr); 1325 1326 ChkErr(GetThemeMetric, kThemeMetricMenuMarkColumnWidth, 1327 &menuMarkColumnWidth); 1328 ChkErr(GetThemeMetric, kThemeMetricMenuTextLeadingEdgeMargin, 1329 &menuTextLeadingEdgeMargin); 1330 ChkErr(GetThemeMetric, kThemeMetricMenuTextTrailingEdgeMargin, 1331 &menuTextTrailingEdgeMargin); 1332 ChkErr(GetThemeMetric, kThemeMetricMenuIconTrailingEdgeMargin, 1333 &menuIconTrailingEdgeMargin); 1334 ChkErr(GetThemeMenuItemExtra, kThemeMenuItemPlain, &menuItemExtraHeight, 1335 &menuItemExtraWidth); 1336 ChkErr(GetThemeMenuSeparatorHeight, &menuSeparatorHeight); 1337} 1338 1339#pragma mark - 1340#pragma mark NOPs 1341 1342/* 1343 *---------------------------------------------------------------------- 1344 * 1345 * TkpMenuThreadInit -- 1346 * 1347 * Does platform-specific initialization of thread-specific menu state. 1348 * 1349 * Results: 1350 * None. 1351 * 1352 * Side effects: 1353 * None. 1354 * 1355 *---------------------------------------------------------------------- 1356 */ 1357 1358void 1359TkpMenuThreadInit(void) 1360{ 1361 /* 1362 * Nothing to do. 1363 */ 1364} 1365 1366/* 1367 *---------------------------------------------------------------------- 1368 * 1369 * TkpMenuNotifyToplevelCreate -- 1370 * 1371 * This routine reconfigures the menu and the clones indicated by 1372 * menuName becuase a toplevel has been created and any system menus need 1373 * to be created. Only applicable to Windows. 1374 * 1375 * Results: 1376 * None. 1377 * 1378 * Side effects: 1379 * An idle handler is set up to do the reconfiguration. 1380 * 1381 *---------------------------------------------------------------------- 1382 */ 1383 1384void 1385TkpMenuNotifyToplevelCreate( 1386 Tcl_Interp *interp, /* The interp the menu lives in. */ 1387 char *menuName) /* The name of the menu to reconfigure. */ 1388{ 1389 /* 1390 * Nothing to do. 1391 */ 1392} 1393 1394/* 1395 *-------------------------------------------------------------- 1396 * 1397 * TkpInitializeMenuBindings -- 1398 * 1399 * For every interp, initializes the bindings for Windows menus. Does 1400 * nothing on Mac or XWindows. 1401 * 1402 * Results: 1403 * None. 1404 * 1405 * Side effects: 1406 * C-level bindings are setup for the interp which will handle Alt-key 1407 * sequences for menus without beeping or interfering with user-defined 1408 * Alt-key bindings. 1409 * 1410 *-------------------------------------------------------------- 1411 */ 1412 1413void 1414TkpInitializeMenuBindings( 1415 Tcl_Interp *interp, /* The interpreter to set. */ 1416 Tk_BindingTable bindingTable) 1417 /* The table to add to. */ 1418{ 1419 /* 1420 * Nothing to do. 1421 */ 1422} 1423 1424/* 1425 *-------------------------------------------------------------- 1426 * 1427 * TkpComputeMenubarGeometry -- 1428 * 1429 * This procedure is invoked to recompute the size and layout of a menu 1430 * that is a menubar clone. 1431 * 1432 * Results: 1433 * None. 1434 * 1435 * Side effects: 1436 * Fields of menu entries are changed to reflect their current positions, 1437 * and the size of the menu window itself may be changed. 1438 * 1439 *-------------------------------------------------------------- 1440 */ 1441 1442void 1443TkpComputeMenubarGeometry( 1444 TkMenu *menuPtr) /* Structure describing menu. */ 1445{ 1446 TkpComputeStandardMenuGeometry(menuPtr); 1447} 1448 1449 1450/* 1451 *---------------------------------------------------------------------- 1452 * 1453 * TkpDrawMenuEntry -- 1454 * 1455 * Draws the given menu entry at the given coordinates with the given 1456 * attributes. 1457 * 1458 * Results: 1459 * None. 1460 * 1461 * Side effects: 1462 * X Server commands are executed to display the menu entry. 1463 * 1464 *---------------------------------------------------------------------- 1465 */ 1466 1467void 1468TkpDrawMenuEntry( 1469 TkMenuEntry *mePtr, /* The entry to draw */ 1470 Drawable d, /* What to draw into */ 1471 Tk_Font tkfont, /* Precalculated font for menu */ 1472 const Tk_FontMetrics *menuMetricsPtr, 1473 /* Precalculated metrics for menu */ 1474 int x, /* X-coordinate of topleft of entry */ 1475 int y, /* Y-coordinate of topleft of entry */ 1476 int width, /* Width of the entry rectangle */ 1477 int height, /* Height of the current rectangle */ 1478 int strictMotif, /* Boolean flag */ 1479 int drawArrow) /* Whether or not to draw the cascade 1480 * arrow for cascade items. Only applies 1481 * to Windows. */ 1482{ 1483} 1484 1485#pragma mark Obsolete 1486 1487/* 1488 *---------------------------------------------------------------------- 1489 * 1490 * TkMacOSXPreprocessMenu -- 1491 * 1492 * Handle preprocessing of menubar if it exists. 1493 * 1494 * Results: 1495 * None. 1496 * 1497 * Side effects: 1498 * All post commands for the current menubar get executed. 1499 * 1500 *---------------------------------------------------------------------- 1501 */ 1502 1503void 1504TkMacOSXPreprocessMenu(void) 1505{ 1506} 1507 1508/* 1509 *---------------------------------------------------------------------- 1510 * 1511 * TkMacOSXUseID -- 1512 * 1513 * Take the ID out of the available list for new menus. Used by the 1514 * default menu bar's menus so that they do not get created at the tk 1515 * level. See TkMacOSXGetNewMenuID for more information. 1516 * 1517 * Results: 1518 * Returns TCL_OK if the id was not in use. Returns TCL_ERROR if the id 1519 * was in use. 1520 * 1521 * Side effects: 1522 * A hash table entry in the command table is created with a NULL value. 1523 * 1524 *---------------------------------------------------------------------- 1525 */ 1526 1527int 1528TkMacOSXUseMenuID( 1529 short macID) /* The id to take out of the table */ 1530{ 1531 return TCL_OK; 1532} 1533 1534/* 1535 *---------------------------------------------------------------------- 1536 * 1537 * TkMacOSXDispatchMenuEvent -- 1538 * 1539 * Given a menu id and an item, dispatches the command associated with 1540 * it. 1541 * 1542 * Results: 1543 * None. 1544 * 1545 * Side effects: 1546 * Commands for the event are scheduled for execution at idle time. 1547 * 1548 *---------------------------------------------------------------------- 1549 */ 1550 1551int 1552TkMacOSXDispatchMenuEvent( 1553 int menuID, /* The menu id of the menu we are invoking */ 1554 int index) /* The one-based index of the item that was 1555 * selected. */ 1556{ 1557 return TCL_ERROR; 1558} 1559 1560/* 1561 *---------------------------------------------------------------------- 1562 * 1563 * TkMacOSXHandleTearoffMenu() -- 1564 * 1565 * This routine sees if the MDEF has set a menu and a mouse position for 1566 * tearing off and makes a tearoff menu if it has. 1567 * 1568 * Results: 1569 * menuPtr->interp will have the result of the tearoff command. 1570 * 1571 * Side effects: 1572 * A new tearoff menu is created if it is supposed to be. 1573 * 1574 *---------------------------------------------------------------------- 1575 */ 1576 1577void 1578TkMacOSXHandleTearoffMenu(void) 1579{ 1580 /* 1581 * Obsolete: Nothing to do. 1582 */ 1583} 1584 1585/* 1586 *---------------------------------------------------------------------- 1587 * 1588 * TkMacOSXSetHelpMenuItemCount -- 1589 * 1590 * Has to be called after the first call to InsertMenu. Sets up the 1591 * global variable for the number of items in the unmodified help menu. 1592 * NB. Nobody uses this any more, since you can get the number of system 1593 * help items from HMGetHelpMenu trivially. But it is in the stubs 1594 * table... 1595 * 1596 * Results: 1597 * None. 1598 * 1599 * Side effects: 1600 * Nothing. 1601 * 1602 *---------------------------------------------------------------------- 1603 */ 1604 1605void 1606TkMacOSXSetHelpMenuItemCount(void) 1607{ 1608} 1609 1610/* 1611 *---------------------------------------------------------------------- 1612 * 1613 * TkMacOSXMenuClick -- 1614 * 1615 * Prepares a menubar for MenuSelect or MenuKey. 1616 * 1617 * Results: 1618 * None. 1619 * 1620 * Side effects: 1621 * Any pending configurations of the menubar are completed. 1622 * 1623 *---------------------------------------------------------------------- 1624 */ 1625 1626void 1627TkMacOSXMenuClick(void) 1628{ 1629} 1630 1631/* 1632 * Local Variables: 1633 * mode: c 1634 * c-basic-offset: 4 1635 * fill-column: 79 1636 * coding: utf-8 1637 * End: 1638 */ 1639