2028 lines
54 KiB
C
2028 lines
54 KiB
C
/*
|
||
* tkMacOSXMenu.c --
|
||
*
|
||
* This module implements the Mac-platform specific features of menus.
|
||
*
|
||
* Copyright (c) 1996-1997 by Sun Microsystems, Inc.
|
||
* Copyright 2001-2009, Apple Inc.
|
||
* Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
|
||
* Copyright (c) 2012 Adrian Robert.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution
|
||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*/
|
||
|
||
#include "tkMacOSXPrivate.h"
|
||
#include "tkMenubutton.h"
|
||
#include "tkMenu.h"
|
||
#include "tkColor.h"
|
||
#include "tkFont.h"
|
||
#include "tkMacOSXWm.h"
|
||
#include "tkMacOSXDebug.h"
|
||
#include "tkMacOSXConstants.h"
|
||
|
||
/*
|
||
#ifdef TK_MAC_DEBUG
|
||
#define TK_MAC_DEBUG_MENUS
|
||
#endif
|
||
*/
|
||
|
||
#define ENTRY_HELP_MENU ENTRY_PLATFORM_FLAG1
|
||
#define ENTRY_APPLE_MENU ENTRY_PLATFORM_FLAG2
|
||
#define ENTRY_WINDOWS_MENU ENTRY_PLATFORM_FLAG3
|
||
|
||
#define sl(s) ((int) (sizeof(s "") - 1))
|
||
|
||
#define SPECIALMENU(n, f) {.name = "." #n, .len = sl(#n) + 1, \
|
||
.flag = ENTRY_##f##_MENU }
|
||
static const struct {
|
||
const char *name; size_t len; int flag;
|
||
} specialMenus[] = {
|
||
SPECIALMENU(help, HELP),
|
||
SPECIALMENU(apple, APPLE),
|
||
SPECIALMENU(window, WINDOWS),
|
||
{NULL, 0, 0}
|
||
};
|
||
#undef SPECIALMENU
|
||
|
||
#define MODIFIER(n, f) {.name = #n, .len = sl(#n), .mask = f }
|
||
static const struct {
|
||
const char *name; size_t len; NSUInteger mask;
|
||
} allModifiers[] = {
|
||
MODIFIER(Control, NSControlKeyMask),
|
||
MODIFIER(Ctrl, NSControlKeyMask),
|
||
MODIFIER(Option, NSAlternateKeyMask),
|
||
MODIFIER(Opt, NSAlternateKeyMask),
|
||
MODIFIER(Alt, NSAlternateKeyMask),
|
||
MODIFIER(Shift, NSShiftKeyMask),
|
||
MODIFIER(Command, NSCommandKeyMask),
|
||
MODIFIER(Cmd, NSCommandKeyMask),
|
||
MODIFIER(Meta, NSCommandKeyMask),
|
||
{NULL, 0, 0}
|
||
};
|
||
#undef MODIFIER
|
||
|
||
#define ACCEL(n, c) {.name = #n, .len = sl(#n), .ch = c }
|
||
static const struct {
|
||
const char *name; size_t len; UniChar ch;
|
||
} specialAccelerators[] = {
|
||
ACCEL(PageUp, NSPageUpFunctionKey),
|
||
ACCEL(PageDown, NSPageDownFunctionKey),
|
||
ACCEL(Left, NSLeftArrowFunctionKey),
|
||
ACCEL(Right, NSRightArrowFunctionKey),
|
||
ACCEL(Up, NSUpArrowFunctionKey),
|
||
ACCEL(Down, NSDownArrowFunctionKey),
|
||
ACCEL(Escape, 0x001b),
|
||
ACCEL(Clear, NSClearDisplayFunctionKey),
|
||
ACCEL(Enter, NSEnterCharacter),
|
||
ACCEL(Backspace, NSBackspaceCharacter),
|
||
ACCEL(Space, ' '),
|
||
ACCEL(Tab, NSTabCharacter),
|
||
ACCEL(BackTab, NSBackTabCharacter),
|
||
ACCEL(Delete, NSDeleteCharacter),
|
||
ACCEL(Home, NSHomeFunctionKey),
|
||
ACCEL(End, NSEndFunctionKey),
|
||
ACCEL(Return, NSCarriageReturnCharacter),
|
||
ACCEL(Help, NSHelpFunctionKey),
|
||
ACCEL(Power, 0x233d),
|
||
ACCEL(Eject, 0xf804),
|
||
{NULL, 0, 0}
|
||
};
|
||
#undef ACCEL
|
||
#undef sl
|
||
|
||
static int gNoTkMenus = 0; /* This is used by Tk_MacOSXTurnOffMenus as
|
||
* the flag that Tk is not to draw any
|
||
* menus. */
|
||
static int inPostMenu = 0;
|
||
static SInt32 menuMarkColumnWidth = 0, menuIconTrailingEdgeMargin = 0;
|
||
static SInt32 menuTextLeadingEdgeMargin = 0, menuTextTrailingEdgeMargin = 0;
|
||
static SInt16 menuItemExtraHeight = 0, menuItemExtraWidth = 0;
|
||
static SInt16 menuSeparatorHeight = 0;
|
||
|
||
static void CheckForSpecialMenu(TkMenu *menuPtr);
|
||
static NSString *ParseAccelerator(const char *accel, NSUInteger *maskPtr);
|
||
static int GenerateMenuSelectEvent(TKMenu *menu, NSMenuItem *menuItem);
|
||
static void MenuSelectEvent(TkMenu *menuPtr);
|
||
static void RecursivelyClearActiveMenu(TkMenu *menuPtr);
|
||
static int ModifierCharWidth(Tk_Font tkfont);
|
||
|
||
#pragma mark TkBackgroundLoop
|
||
|
||
/*
|
||
* The function TkMacOSXEventsCheckProc (in tkMacOSXNotify.c) is the "check
|
||
* proc" for the macOS event source. Its job is to remove NSEvents from the
|
||
* default event queue of the NSApplication. It does this by calling the
|
||
* method [NSApp nextEventMatchingMask: untilDate: inMode: dequeue:]. As a
|
||
* rule, when the untilDate is set to the distant past this method returns
|
||
* immediately. An exception to that rule is when the next event is the button
|
||
* press on a menu button. In that case, the method starts running a nested
|
||
* event loop in the mode NSEventTrackingRunLoopMode which does not return
|
||
* until the menu has been dismissed. In Tk 8.6.10 and earlier, this meant
|
||
* that the Tk event loop would block in its call to the check proc as long as
|
||
* the menu was posted. For example, opening a menu during the Rube Goldberg
|
||
* demo would cause the animation to stop. This was also the case for
|
||
* menubuttons.
|
||
*
|
||
* The TKBackground object below works around this problem, and allows a Tk
|
||
* event loop to run while a menu is open. It is a subclass of NSThread which
|
||
* inserts requests to call [NSApp _runBackgroundLoop] onto the queue
|
||
* associated with the NSEventTrackingRunLoopMode. One of these threads gets
|
||
* started in the callback [NSApp menuBeginTracking] and cancelled in [NSApp
|
||
* menuEndTracking].
|
||
*/
|
||
|
||
@interface TKBackgroundLoop: NSThread
|
||
@end
|
||
|
||
@implementation TKBackgroundLoop
|
||
- (void) main
|
||
{
|
||
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||
NSArray *modeArray = [NSArray arrayWithObjects: NSEventTrackingRunLoopMode,
|
||
nil];
|
||
while(1) {
|
||
|
||
/*
|
||
* Queue a request to process Tk events during event tracking.
|
||
*/
|
||
|
||
[NSApp performSelectorOnMainThread:@selector(_runBackgroundLoop)
|
||
withObject:nil
|
||
waitUntilDone:true
|
||
modes:modeArray];
|
||
if ([self isCancelled]) {
|
||
[NSThread exit];
|
||
}
|
||
|
||
/*
|
||
* Allow the tracked events to be processed too.
|
||
*/
|
||
|
||
[NSThread sleepForTimeInterval:0.001];
|
||
}
|
||
[pool drain];
|
||
}
|
||
@end
|
||
|
||
TKBackgroundLoop *backgroundLoop = nil;
|
||
|
||
|
||
#pragma mark TKMenu
|
||
|
||
/*
|
||
* This interface is not declared in tkMacOSXPrivate.h because it requires
|
||
* tkMenu.h.
|
||
*/
|
||
|
||
@interface TKMenu(TKMenuPrivate)
|
||
- (id) initWithTkMenu: (TkMenu *) tkMenu;
|
||
- (TkMenu *) tkMenu;
|
||
- (int) tkIndexOfItem: (NSMenuItem *) menuItem;
|
||
- (void) insertItem: (NSMenuItem *) newItem atTkIndex: (NSInteger) index;
|
||
@end
|
||
|
||
@implementation TKMenu
|
||
- (void) setSpecial: (NSUInteger) special
|
||
{
|
||
NSAssert(!_tkSpecial, @"Cannot change specialness of a special menu");
|
||
_tkSpecial = special;
|
||
}
|
||
- (BOOL) isSpecial: (NSUInteger) special
|
||
{
|
||
return (_tkSpecial == special);
|
||
}
|
||
@end
|
||
|
||
@implementation TKMenu(TKMenuPrivate)
|
||
|
||
- (id) initWithTitle: (NSString *) aTitle
|
||
{
|
||
self = [super initWithTitle:aTitle];
|
||
if (self) {
|
||
_tkMenu = NULL;
|
||
_tkOffset = 0;
|
||
_tkItemCount = 0;
|
||
_tkSpecial = 0;
|
||
[self setDelegate:self];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (id) initWithTkMenu: (TkMenu *) tkMenu
|
||
{
|
||
NSString *title = [[NSString alloc] initWithUTF8String:
|
||
Tk_PathName(tkMenu->tkwin)];
|
||
|
||
self = [self initWithTitle:title];
|
||
[title release];
|
||
if (self) {
|
||
_tkMenu = tkMenu;
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (id) copyWithZone: (NSZone *) zone
|
||
{
|
||
TKMenu *copy = [super copyWithZone:zone];
|
||
|
||
NSAssert(_tkMenu == nil, @"Cannot copy tkMenu");
|
||
copy->_tkMenu = _tkMenu;
|
||
copy->_tkOffset = _tkOffset;
|
||
copy->_tkItemCount = _tkItemCount;
|
||
copy->_tkSpecial = _tkSpecial;
|
||
return copy;
|
||
}
|
||
|
||
- (TkMenu *) tkMenu
|
||
{
|
||
return _tkMenu;
|
||
}
|
||
|
||
- (int) tkIndexOfItem: (NSMenuItem *) menuItem
|
||
{
|
||
return [self indexOfItem:menuItem] - _tkOffset;
|
||
}
|
||
|
||
- (void) insertItem: (NSMenuItem *) newItem atTkIndex: (NSInteger) index
|
||
{
|
||
[super insertItem:newItem atIndex:index + _tkOffset];
|
||
_tkItemCount++;
|
||
}
|
||
|
||
- (void) insertItem: (NSMenuItem *) newItem atIndex: (NSInteger) index
|
||
{
|
||
if (_tkMenu && index >= 0) {
|
||
if ((NSUInteger) index <= _tkOffset) {
|
||
_tkOffset++;
|
||
} else {
|
||
NSAssert((NSUInteger) index >= _tkItemCount + _tkOffset,
|
||
@"Cannot insert in the middle of Tk menu");
|
||
}
|
||
}
|
||
[super insertItem:newItem atIndex:index];
|
||
}
|
||
|
||
- (void) removeItemAtIndex: (NSInteger) index
|
||
{
|
||
if (_tkMenu && index >= 0) {
|
||
if ((NSUInteger) index < _tkOffset) {
|
||
_tkOffset--;
|
||
} else if ((NSUInteger) index < _tkItemCount + _tkOffset) {
|
||
_tkItemCount--;
|
||
}
|
||
}
|
||
[super removeItemAtIndex:index];
|
||
}
|
||
|
||
- (NSMenuItem *) newTkMenuItem: (TkMenuEntry *) mePtr
|
||
{
|
||
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:@""
|
||
action:@selector(tkMenuItemInvoke:) keyEquivalent:@""];
|
||
|
||
[menuItem setTarget:self];
|
||
[menuItem setTag:(NSInteger) mePtr];
|
||
return menuItem;
|
||
}
|
||
@end
|
||
|
||
@implementation TKMenu(TKMenuActions)
|
||
|
||
- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
|
||
{
|
||
return [menuItem isEnabled];
|
||
}
|
||
|
||
/*
|
||
* Workaround for bug 3572016; leave menu items enabled during modal dialog.
|
||
*/
|
||
|
||
- (BOOL)worksWhenModal
|
||
{
|
||
return YES;
|
||
}
|
||
|
||
- (void) tkMenuItemInvoke: (id) sender
|
||
{
|
||
/*
|
||
* With the delegate matching key equivalents, when a menu action is sent
|
||
* in response to a key equivalent, the sender is the whole menu and not the
|
||
* specific menu item. We use this to ignore key equivalents for Tk
|
||
* menus (as Tk handles them directly via bindings).
|
||
*/
|
||
|
||
if ([sender isKindOfClass:[NSMenuItem class]]) {
|
||
NSMenuItem *menuItem = (NSMenuItem *) sender;
|
||
TkMenu *menuPtr = (TkMenu *) _tkMenu;
|
||
TkMenuEntry *mePtr = (TkMenuEntry *) [menuItem tag];
|
||
|
||
if (menuPtr && mePtr) {
|
||
Tcl_Interp *interp = menuPtr->interp;
|
||
|
||
Tcl_Preserve(interp);
|
||
Tcl_Preserve(menuPtr);
|
||
|
||
int result = TkInvokeMenu(interp, menuPtr, mePtr->index);
|
||
|
||
if (result != TCL_OK && result != TCL_CONTINUE &&
|
||
result != TCL_BREAK) {
|
||
Tcl_AddErrorInfo(interp, "\n (menu invoke)");
|
||
Tcl_BackgroundException(interp, result);
|
||
}
|
||
Tcl_Release(menuPtr);
|
||
Tcl_Release(interp);
|
||
}
|
||
}
|
||
}
|
||
@end
|
||
|
||
@implementation TKMenu(TKMenuDelegate)
|
||
|
||
- (BOOL) menuHasKeyEquivalent: (NSMenu *) menu forEvent: (NSEvent *) event
|
||
target: (id *) target action: (SEL *) action
|
||
{
|
||
(void)menu;
|
||
|
||
/*
|
||
* Use lowercaseString when comparing keyEquivalents since the notion of
|
||
* a shifted upper case letter does not make much sense.
|
||
*/
|
||
|
||
NSString *key = [[event charactersIgnoringModifiers] lowercaseString];
|
||
NSUInteger modifiers = [event modifierFlags] &
|
||
NSDeviceIndependentModifierFlagsMask;
|
||
|
||
if (modifiers == (NSCommandKeyMask | NSShiftKeyMask) &&
|
||
[key compare:@"?"] == NSOrderedSame) {
|
||
/*
|
||
* Command-Shift-? has not been allowed as a keyboard equivalent since
|
||
* the first aqua port, for some mysterious reason.
|
||
*/
|
||
|
||
return NO;
|
||
} else if (modifiers == (NSControlKeyMask | NSShiftKeyMask) &&
|
||
[event keyCode] == 48) {
|
||
/*
|
||
* Starting with OSX 10.12 Control-Tab and Control-Shift-Tab are used
|
||
* to select window tabs. But for some even more mysterious reason the
|
||
* Control-Shift-Tab event has character 0x19 = NSBackTabCharacter
|
||
* rather than 0x09 = NSTabCharacter. At the same time, the
|
||
* keyEquivalent must be \0x09 in order for it to be displayed
|
||
* correctly in the menu. This makes it impossible for the standard
|
||
* "Select Previous Tab" to work correctly, unless we intervene.
|
||
*/
|
||
|
||
key = @"\t";
|
||
} else if (([event modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask) {
|
||
/*
|
||
* If the command modifier is set, use the full character string so
|
||
* things like the dvorak / qwerty layout will work.
|
||
*/
|
||
|
||
key = [event characters];
|
||
}
|
||
|
||
NSArray *itemArray = [self itemArray];
|
||
for (NSMenuItem *item in itemArray) {
|
||
if ([item isEnabled] &&
|
||
[[item keyEquivalent] compare:key] == NSOrderedSame) {
|
||
NSUInteger keyEquivModifiers = [item keyEquivalentModifierMask];
|
||
if (keyEquivModifiers == modifiers) {
|
||
*target = [item target];
|
||
*action = [item action];
|
||
return YES;
|
||
}
|
||
}
|
||
}
|
||
return NO;
|
||
}
|
||
|
||
- (void) menuWillOpen: (NSMenu *) menu
|
||
{
|
||
(void)menu;
|
||
|
||
if (_tkMenu) {
|
||
//RecursivelyClearActiveMenu(_tkMenu);
|
||
GenerateMenuSelectEvent((TKMenu *)[self supermenu],
|
||
[self itemInSupermenu]);
|
||
}
|
||
}
|
||
|
||
- (void) menuDidClose: (NSMenu *) menu
|
||
{
|
||
(void)menu;
|
||
|
||
if (_tkMenu) {
|
||
RecursivelyClearActiveMenu(_tkMenu);
|
||
}
|
||
}
|
||
|
||
- (void) menu: (NSMenu *) menu willHighlightItem: (NSMenuItem *) item
|
||
{
|
||
(void)menu;
|
||
|
||
if (_tkMenu) {
|
||
GenerateMenuSelectEvent(self, item);
|
||
}
|
||
}
|
||
|
||
- (void) menuNeedsUpdate: (NSMenu *) menu
|
||
{
|
||
TkMenu *menuPtr = (TkMenu *) _tkMenu;
|
||
(void)menu;
|
||
|
||
if (menuPtr) {
|
||
Tcl_Interp *interp = menuPtr->interp;
|
||
|
||
Tcl_Preserve(interp);
|
||
Tcl_Preserve(menuPtr);
|
||
|
||
int result = TkPostCommand(_tkMenu);
|
||
|
||
if (result!=TCL_OK && result!=TCL_CONTINUE && result!=TCL_BREAK) {
|
||
Tcl_AddErrorInfo(interp, "\n (menu preprocess)");
|
||
Tcl_BackgroundException(interp, result);
|
||
}
|
||
Tcl_Release(menuPtr);
|
||
Tcl_Release(interp);
|
||
}
|
||
}
|
||
@end
|
||
|
||
#pragma mark TKApplication(TKMenu)
|
||
|
||
@implementation TKApplication(TKMenu)
|
||
|
||
- (void) menuBeginTracking: (NSNotification *) notification
|
||
{
|
||
(void)notification;
|
||
#ifdef TK_MAC_DEBUG_NOTIFICATIONS
|
||
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
|
||
#endif
|
||
if (backgroundLoop) {
|
||
[backgroundLoop cancel];
|
||
[backgroundLoop release];
|
||
}
|
||
backgroundLoop = [[TKBackgroundLoop alloc] init];
|
||
[backgroundLoop start];
|
||
//TkMacOSXClearMenubarActive();
|
||
//TkMacOSXPreprocessMenu();
|
||
}
|
||
|
||
- (void) menuEndTracking: (NSNotification *) notification
|
||
{
|
||
(void)notification;
|
||
#ifdef TK_MAC_DEBUG_NOTIFICATIONS
|
||
TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, notification);
|
||
#endif
|
||
if (backgroundLoop) {
|
||
[backgroundLoop cancel];
|
||
[backgroundLoop release];
|
||
backgroundLoop = nil;
|
||
}
|
||
if (!inPostMenu) {
|
||
TkMacOSXClearMenubarActive();
|
||
}
|
||
}
|
||
|
||
- (void) tkSetMainMenu: (TKMenu *) menu
|
||
{
|
||
if (gNoTkMenus) {
|
||
return;
|
||
}
|
||
|
||
TKMenu *applicationMenu = nil;
|
||
|
||
if (menu) {
|
||
NSMenuItem *applicationMenuItem = [menu numberOfItems] ?
|
||
[menu itemAtIndex:0] : nil;
|
||
|
||
if (![menu isSpecial:tkMainMenu]) {
|
||
TkMenuEntry *mePtr = (TkMenuEntry *)[applicationMenuItem tag];
|
||
|
||
if (!mePtr || !(mePtr->entryFlags & ENTRY_APPLE_MENU)) {
|
||
applicationMenuItem = [NSMenuItem itemWithSubmenu:
|
||
[_defaultApplicationMenu copy]];
|
||
[menu insertItem:applicationMenuItem atIndex:0];
|
||
}
|
||
[menu setSpecial:tkMainMenu];
|
||
}
|
||
applicationMenu = (TKMenu *)[applicationMenuItem submenu];
|
||
if (![applicationMenu isSpecial:tkApplicationMenu]) {
|
||
for (NSMenuItem *item in _defaultApplicationMenuItems) {
|
||
[applicationMenu addItem:[item copy]];
|
||
}
|
||
[applicationMenu setSpecial:tkApplicationMenu];
|
||
}
|
||
|
||
NSArray *itemArray = [menu itemArray];
|
||
|
||
for (NSMenuItem *item in itemArray) {
|
||
TkMenuEntry *mePtr = (TkMenuEntry *)[item tag];
|
||
TKMenu *submenu = (TKMenu *)[item submenu];
|
||
if (mePtr && submenu) {
|
||
if ((mePtr->entryFlags & ENTRY_WINDOWS_MENU) &&
|
||
![submenu isSpecial:tkWindowsMenu]) {
|
||
NSInteger index = 0;
|
||
|
||
for (NSMenuItem *i in _defaultWindowsMenuItems) {
|
||
[submenu insertItem:[i copy] atIndex:index++];
|
||
}
|
||
[self setWindowsMenu:submenu];
|
||
[submenu setSpecial:tkWindowsMenu];
|
||
} else if ((mePtr->entryFlags & ENTRY_HELP_MENU) &&
|
||
![submenu isSpecial:tkHelpMenu]) {
|
||
NSInteger index = 0;
|
||
|
||
for (NSMenuItem *i in _defaultHelpMenuItems) {
|
||
[submenu insertItem:[i copy] atIndex:index++];
|
||
}
|
||
[submenu setSpecial:tkHelpMenu];
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
menu = _defaultMainMenu;
|
||
applicationMenu = _defaultApplicationMenu;
|
||
}
|
||
|
||
NSMenuItem *servicesMenuItem =
|
||
[applicationMenu itemWithTitle:@"Services"];
|
||
|
||
if (servicesMenuItem && [servicesMenuItem submenu] != _servicesMenu) {
|
||
[[_servicesMenu itemInSupermenu] setSubmenu:nil];
|
||
[servicesMenuItem setSubmenu:_servicesMenu];
|
||
}
|
||
[self setAppleMenu:applicationMenu];
|
||
[self setMainMenu:menu];
|
||
}
|
||
@end
|
||
|
||
#pragma mark -
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpNewMenu --
|
||
*
|
||
* Gets a new blank menu. Only the platform specific options are filled
|
||
* in.
|
||
*
|
||
* Results:
|
||
* Returns a standard Tcl error.
|
||
*
|
||
* Side effects:
|
||
* Allocates a NSMenu and puts it into the platformData field of the
|
||
* menuPtr.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkpNewMenu(
|
||
TkMenu *menuPtr) /* The common structure we are making the
|
||
* platform structure for. */
|
||
{
|
||
TKMenu *menu = [[TKMenu alloc] initWithTkMenu:menuPtr];
|
||
menuPtr->platformData = (TkMenuPlatformData) menu;
|
||
CheckForSpecialMenu(menuPtr);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpDestroyMenu --
|
||
*
|
||
* Destroys platform-specific menu structures.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* All platform-specific allocations are freed up.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpDestroyMenu(
|
||
TkMenu *menuPtr) /* The common menu structure */
|
||
{
|
||
NSMenu* nsmenu = (NSMenu*)(menuPtr->platformData);
|
||
|
||
[nsmenu release];
|
||
menuPtr->platformData = NULL;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpMenuNewEntry --
|
||
*
|
||
* Adds a pointer to a new menu entry structure with the platform-
|
||
* specific fields filled in. The Macintosh uses the platformEntryData
|
||
* field of the TkMenuEntry record.
|
||
*
|
||
* Results:
|
||
* Standard TCL error.
|
||
*
|
||
* Side effects:
|
||
* Storage gets allocated. New menu entry data is put into the
|
||
* platformEntryData field of the mePtr.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkpMenuNewEntry(
|
||
TkMenuEntry *mePtr) /* The menu we are adding an entry to */
|
||
{
|
||
TKMenu *menu = (TKMenu *) mePtr->menuPtr->platformData;
|
||
NSMenuItem *menuItem;
|
||
if (mePtr->type == SEPARATOR_ENTRY || mePtr->type == TEAROFF_ENTRY) {
|
||
menuItem = [[NSMenuItem separatorItem] retain];
|
||
} else {
|
||
menuItem = [menu newTkMenuItem:mePtr];
|
||
}
|
||
mePtr->platformEntryData = (TkMenuPlatformEntryData) menuItem;
|
||
|
||
/*
|
||
* Caller TkMenuEntry() already did this same insertion into the generic
|
||
* TkMenu so we just match it for the platform menu.
|
||
*/
|
||
|
||
[menu insertItem:menuItem atTkIndex:mePtr->index];
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpConfigureMenuEntry --
|
||
*
|
||
* Processes configurations for menu entries.
|
||
*
|
||
* Results:
|
||
* Returns standard TCL result. If TCL_ERROR is returned, then the
|
||
* interp's result contains an error message.
|
||
*
|
||
* Side effects:
|
||
* Configuration information get set for mePtr; old resources get freed,
|
||
* if any need it.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkpConfigureMenuEntry(
|
||
TkMenuEntry *mePtr) /* Information about menu entry; may or may
|
||
* not already have values for some fields. */
|
||
{
|
||
NSMenuItem *menuItem = (NSMenuItem *) mePtr->platformEntryData;
|
||
NSString *title = @"";
|
||
NSAttributedString *attributedTitle = nil;
|
||
NSImage *image = nil;
|
||
NSString *keyEquivalent = @"";
|
||
NSUInteger modifierMask = NSCommandKeyMask;
|
||
NSMenu *submenu = nil;
|
||
int imageWidth, imageHeight;
|
||
GC gc = (mePtr->textGC ? mePtr->textGC : mePtr->menuPtr->textGC);
|
||
Tcl_Obj *fontPtr = (mePtr->fontPtr ? mePtr->fontPtr :
|
||
mePtr->menuPtr->fontPtr);
|
||
|
||
if (mePtr->image) {
|
||
Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
|
||
image = TkMacOSXGetNSImageFromTkImage(mePtr->menuPtr->display,
|
||
mePtr->image, imageWidth, imageHeight);
|
||
} else if (mePtr->bitmapPtr != None) {
|
||
Pixmap bitmap = Tk_GetBitmapFromObj(mePtr->menuPtr->tkwin,
|
||
mePtr->bitmapPtr);
|
||
|
||
Tk_SizeOfBitmap(mePtr->menuPtr->display, bitmap, &imageWidth,
|
||
&imageHeight);
|
||
image = TkMacOSXGetNSImageFromBitmap(mePtr->menuPtr->display, bitmap,
|
||
gc, imageWidth, imageHeight);
|
||
[image setTemplate:YES];
|
||
}
|
||
[menuItem setImage:image];
|
||
if ((!image || mePtr->compound != COMPOUND_NONE) && mePtr->labelPtr &&
|
||
mePtr->labelLength) {
|
||
title = [[[NSString alloc] initWithBytes:Tcl_GetString(mePtr->labelPtr)
|
||
length:mePtr->labelLength encoding:NSUTF8StringEncoding]
|
||
autorelease];
|
||
if ([title hasSuffix:@"..."]) {
|
||
title = [NSString stringWithFormat:@"%@%C",
|
||
[title substringToIndex:[title length] - 3], 0x2026];
|
||
}
|
||
}
|
||
[menuItem setTitle:title];
|
||
|
||
#if 0
|
||
|
||
/*
|
||
* The -background and -foreground options are now ignored in Aqua.
|
||
* See ticket [635167af14].
|
||
*/
|
||
|
||
NSDictionary fontAttributes = TkMacOSXNSFontAttributesForFont(
|
||
Tk_GetFontFromObj(mePtr->menuPtr->tkwin, fontPtr));
|
||
NSMutableDictionary *attributes = [fontAttributes mutableCopy];
|
||
static unsigned long defaultBg = 0, defaultFg = 0;
|
||
if (defaultBg == 0) {
|
||
tkColor *tkColPtr = TkpGetColor(NULL, DEF_MENU_BG_COLOR);
|
||
defaultBg = tkColPtr->color.pixel;
|
||
ckfree(tkColPtr);
|
||
}
|
||
if (defaultFg == 0) {
|
||
tkColor *tkColPtr = TkpGetColor(NULL, DEF_MENU_FG);
|
||
defaultFg = tkColPtr->color.pixel;
|
||
ckfree(tkColPtr);
|
||
}
|
||
if (gc->foreground != defaultFg) {
|
||
NSColor *fgcolor = TkMacOSXGetNSColor(gc, gc->foreground);
|
||
[attributes setObject:fgcolor
|
||
forKey:NSForegroundColorAttributeName];
|
||
}
|
||
if (gc->background != defaultBg) {
|
||
NSColor *bgcolor = TkMacOSXGetNSColor(gc, gc->background);
|
||
[attributes setObject:bgcolor
|
||
forKey:NSBackgroundColorAttributeName];
|
||
}
|
||
|
||
#else
|
||
|
||
NSDictionary *attributes = TkMacOSXNSFontAttributesForFont(
|
||
Tk_GetFontFromObj(mePtr->menuPtr->tkwin, fontPtr));
|
||
|
||
#endif
|
||
|
||
attributedTitle = [[NSAttributedString alloc] initWithString:title
|
||
attributes:attributes];
|
||
[menuItem setAttributedTitle:attributedTitle];
|
||
[menuItem setEnabled:(mePtr->state != ENTRY_DISABLED)];
|
||
[menuItem setState:((mePtr->type == CHECK_BUTTON_ENTRY ||
|
||
mePtr->type == RADIO_BUTTON_ENTRY) && mePtr->indicatorOn &&
|
||
(mePtr->entryFlags & ENTRY_SELECTED) ? NSOnState : NSOffState)];
|
||
if (mePtr->type != CASCADE_ENTRY && mePtr->accelPtr && mePtr->accelLength) {
|
||
keyEquivalent = ParseAccelerator(Tcl_GetString(mePtr->accelPtr),
|
||
&modifierMask);
|
||
}
|
||
[menuItem setKeyEquivalent:keyEquivalent];
|
||
[menuItem setKeyEquivalentModifierMask:modifierMask];
|
||
if (mePtr->type == CASCADE_ENTRY && mePtr->namePtr) {
|
||
TkMenuReferences *menuRefPtr;
|
||
|
||
menuRefPtr = TkFindMenuReferencesObj(mePtr->menuPtr->interp,
|
||
mePtr->namePtr);
|
||
if (menuRefPtr && menuRefPtr->menuPtr) {
|
||
CheckForSpecialMenu(menuRefPtr->menuPtr);
|
||
submenu = (TKMenu *) menuRefPtr->menuPtr->platformData;
|
||
if ([submenu supermenu] && [menuItem submenu] != submenu) {
|
||
/*
|
||
* This happens during a clone, where the parent menu is
|
||
* cloned before its children, so just ignore this temprary
|
||
* setting, it will be changed shortly (c.f. tkMenu.c
|
||
* CloneMenu())
|
||
*/
|
||
|
||
submenu = nil;
|
||
} else {
|
||
[submenu setTitle:title];
|
||
|
||
if ([menuItem isEnabled]) {
|
||
|
||
/*
|
||
* This menuItem might have been previously disabled which
|
||
* would have disabled all of its entries; we must re-enable the
|
||
* entries here. It is important to iterate though the Tk
|
||
* entries, not the NSMenuItems, since some NSMenuItems may
|
||
* have been added by the system. See [7185d26cf4].
|
||
*/
|
||
|
||
for (int i = 0; i < menuRefPtr->menuPtr->numEntries; i++) {
|
||
TkMenuEntry *submePtr = menuRefPtr->menuPtr->entries[i];
|
||
NSMenuItem *item = (NSMenuItem *) submePtr->platformEntryData;
|
||
[item setEnabled:(submePtr->state != ENTRY_DISABLED)];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
[menuItem setSubmenu:submenu];
|
||
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpDestroyMenuEntry --
|
||
*
|
||
* Cleans up platform-specific menu entry items.
|
||
*
|
||
* Results:
|
||
* None
|
||
*
|
||
* Side effects:
|
||
* All platform-specific allocations are freed up.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpDestroyMenuEntry(
|
||
TkMenuEntry *mePtr)
|
||
{
|
||
NSMenuItem *menuItem;
|
||
TKMenu *menu;
|
||
NSInteger index;
|
||
|
||
if (mePtr->platformEntryData && mePtr->menuPtr->platformData) {
|
||
menu = (TKMenu *) mePtr->menuPtr->platformData;
|
||
menuItem = (NSMenuItem *) mePtr->platformEntryData;
|
||
index = [menu indexOfItem:menuItem];
|
||
|
||
if (index > -1) {
|
||
[menu removeItemAtIndex:index];
|
||
}
|
||
[menuItem release];
|
||
mePtr->platformEntryData = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpPostMenu --
|
||
*
|
||
* Posts a menu on the screen. If entry is < 0 then the menu is drawn so
|
||
* its top left corner is located at the point with screen coordinates
|
||
* (x,y). Otherwise the top left corner of the specified entry is located
|
||
* at that point.
|
||
*
|
||
* Results:
|
||
* Returns a standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* The menu is posted and handled.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkpPostMenu(
|
||
TCL_UNUSED(Tcl_Interp *), /* The interpreter this menu lives in */
|
||
TkMenu *menuPtr, /* The menu we are posting */
|
||
int x, int y, /* The screen coordinates where the top left
|
||
* corner of the menu, or of the specified
|
||
* entry, will be located. */
|
||
int index)
|
||
{
|
||
int result;
|
||
Tk_Window realWin = menuPtr->tkwin;
|
||
TkWindow *realWinPtr;
|
||
NSView *realWinView;
|
||
|
||
while (1) {
|
||
if (realWin == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
/*
|
||
* Fix for bug 07cfc9f03e: use the view for the parent real (non-menu)
|
||
* toplevel window, rather than always using the root window.
|
||
* This allows menus to appear on a separate monitor than the root
|
||
* window, and to use the appearance of their parent real window
|
||
* rather than the appearance of the root window.
|
||
*/
|
||
realWinPtr = (TkWindow*) realWin;
|
||
realWinView = TkMacOSXGetNSViewForDrawable(realWinPtr->privatePtr);
|
||
if (realWinView != nil) {
|
||
break;
|
||
}
|
||
realWin = Tk_Parent(realWin);
|
||
}
|
||
NSWindow *win = [realWinView window];
|
||
NSView *view = [win contentView];
|
||
NSMenu *menu = (NSMenu *) menuPtr->platformData;
|
||
NSInteger itemIndex = index;
|
||
NSInteger numItems = [menu numberOfItems];
|
||
NSMenuItem *item = nil;
|
||
NSPoint location = NSMakePoint(x, TkMacOSXZeroScreenHeight() - y);
|
||
|
||
inPostMenu = 1;
|
||
result = TkPreprocessMenu(menuPtr);
|
||
if (result != TCL_OK) {
|
||
inPostMenu = 0;
|
||
return result;
|
||
}
|
||
if (itemIndex >= numItems) {
|
||
itemIndex = numItems - 1;
|
||
}
|
||
if (itemIndex >= 0) {
|
||
item = [menu itemAtIndex:itemIndex];
|
||
}
|
||
|
||
/*
|
||
* The post commands could have deleted the menu, which means we are dead
|
||
* and should go away.
|
||
*/
|
||
|
||
if (menuPtr->tkwin == NULL) {
|
||
return TCL_OK;
|
||
}
|
||
|
||
[menu popUpMenuPositioningItem:item
|
||
atLocation:[win tkConvertPointFromScreen:location]
|
||
inView:view];
|
||
inPostMenu = 0;
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpPostTearoffMenu --
|
||
*
|
||
* Tearoff menus are not supported on the Mac. This placeholder function,
|
||
* which is simply a copy of the unix function, posts a completely useless
|
||
* window with a black background on the screen. If entry is < 0 then the
|
||
* window is positioned so that its top left corner is located at the
|
||
* point with screen coordinates (x, y). Otherwise the window position is
|
||
* offset so that top left corner of the specified entry would be located
|
||
* at that point, if there actually were a menu.
|
||
*
|
||
* Mac menus steal all mouse or keyboard input from the application until
|
||
* the menu is dismissed, with or without a selection, by a mouse or key
|
||
* event. Posting a Mac menu in a regression test will cause the test to
|
||
* halt waiting for user input. This is why the TkpPostMenu function is
|
||
* not being used as the placeholder.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* A useless window is posted.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkpPostTearoffMenu(
|
||
TCL_UNUSED(Tcl_Interp *), /* The interpreter this menu lives in */
|
||
TkMenu *menuPtr, /* The menu we are posting */
|
||
int x, int y, int index) /* The screen coordinates where the top left
|
||
* corner of the menu, or of the specified
|
||
* entry, will be located. */
|
||
{
|
||
int vRootX, vRootY, vRootWidth, vRootHeight;
|
||
int result;
|
||
|
||
if (index >= (int) menuPtr->numEntries) {
|
||
index = menuPtr->numEntries - 1;
|
||
}
|
||
if (index >= 0) {
|
||
y -= menuPtr->entries[index]->y;
|
||
}
|
||
|
||
TkActivateMenuEntry(menuPtr, -1);
|
||
TkRecomputeMenu(menuPtr);
|
||
result = TkPostCommand(menuPtr);
|
||
if (result != TCL_OK) {
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
* The post commands could have deleted the menu, which means we are dead
|
||
* and should go away.
|
||
*/
|
||
|
||
if (menuPtr->tkwin == NULL) {
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
* Adjust the position of the menu if necessary to keep it visible on the
|
||
* screen. There are two special tricks to make this work right:
|
||
*
|
||
* 1. If a virtual root window manager is being used then the coordinates
|
||
* are in the virtual root window of menuPtr's parent; since the menu
|
||
* uses override-redirect mode it will be in the *real* root window for
|
||
* the screen, so we have to map the coordinates from the virtual root
|
||
* (if any) to the real root. Can't get the virtual root from the menu
|
||
* itself (it will never be seen by the wm) so use its parent instead
|
||
* (it would be better to have an an option that names a window to use
|
||
* for this...).
|
||
* 2. The menu may not have been mapped yet, so its current size might be
|
||
* the default 1x1. To compute how much space it needs, use its
|
||
* requested size, not its actual size.
|
||
*/
|
||
|
||
Tk_GetVRootGeometry(Tk_Parent(menuPtr->tkwin), &vRootX, &vRootY,
|
||
&vRootWidth, &vRootHeight);
|
||
vRootWidth -= Tk_ReqWidth(menuPtr->tkwin);
|
||
if (x > vRootX + vRootWidth) {
|
||
x = vRootX + vRootWidth;
|
||
}
|
||
if (x < vRootX) {
|
||
x = vRootX;
|
||
}
|
||
vRootHeight -= Tk_ReqHeight(menuPtr->tkwin);
|
||
if (y > vRootY + vRootHeight) {
|
||
y = vRootY + vRootHeight;
|
||
}
|
||
if (y < vRootY) {
|
||
y = vRootY;
|
||
}
|
||
Tk_MoveToplevelWindow(menuPtr->tkwin, x, y);
|
||
if (!Tk_IsMapped(menuPtr->tkwin)) {
|
||
Tk_MapWindow(menuPtr->tkwin);
|
||
}
|
||
TkWmRestackToplevel((TkWindow *) menuPtr->tkwin, Above, NULL);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpSetWindowMenuBar --
|
||
*
|
||
* Associates a given menu with a window.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* On Windows and UNIX, associates the platform menu with the platform
|
||
* window.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpSetWindowMenuBar(
|
||
Tk_Window tkwin, /* The window we are setting the menu in */
|
||
TkMenu *menuPtr) /* The menu we are setting */
|
||
{
|
||
TkWindow *winPtr = (TkWindow *) tkwin;
|
||
|
||
if (winPtr->wmInfoPtr) {
|
||
winPtr->wmInfoPtr->menuPtr = menuPtr;
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpSetMainMenubar --
|
||
*
|
||
* Puts the menu associated with a window into the menubar. Should only be
|
||
* called when the window is in front.
|
||
*
|
||
* This is a no-op on all other platforms. On OS X it is a no-op when
|
||
* passed a NULL menuName or a nonexistent menuName, with an exception for
|
||
* the first call in a new interpreter. In that special case, passing a
|
||
* NULL menuName installs the default menu.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The menubar may be changed.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpSetMainMenubar(
|
||
Tcl_Interp *interp, /* The interpreter of the application */
|
||
Tk_Window tkwin, /* The frame we are setting up */
|
||
const char *menuName) /* The name of the menu to put in front. */
|
||
{
|
||
static Tcl_Interp *currentInterp = NULL;
|
||
TKMenu *menu = nil;
|
||
TkWindow *winPtr = (TkWindow *) tkwin;
|
||
|
||
/*
|
||
* We will be called when an embedded window receives an ActivationNotify
|
||
* event, but we should not change the menubar in that case.
|
||
*/
|
||
|
||
if (Tk_IsEmbedded(winPtr)) {
|
||
return;
|
||
}
|
||
|
||
if (menuName) {
|
||
Tk_Window menubar = NULL;
|
||
|
||
if (winPtr->wmInfoPtr &&
|
||
winPtr->wmInfoPtr->menuPtr &&
|
||
winPtr->wmInfoPtr->menuPtr->masterMenuPtr) {
|
||
menubar = winPtr->wmInfoPtr->menuPtr->masterMenuPtr->tkwin;
|
||
}
|
||
|
||
/*
|
||
* Attempt to find the NSMenu directly. If that fails, ask Tk to find
|
||
* it.
|
||
*/
|
||
|
||
if (menubar != NULL && strcmp(menuName, Tk_PathName(menubar)) == 0) {
|
||
menu = (TKMenu *) winPtr->wmInfoPtr->menuPtr->platformData;
|
||
} else {
|
||
TkMenuReferences *menuRefPtr = TkFindMenuReferences(interp,
|
||
menuName);
|
||
|
||
if (menuRefPtr && menuRefPtr->menuPtr &&
|
||
menuRefPtr->menuPtr->platformData) {
|
||
menu = (TKMenu *) menuRefPtr->menuPtr->platformData;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* If we couldn't find a menu, do nothing unless the window belongs to a
|
||
* different application. In that case, install the default menubar.
|
||
*/
|
||
|
||
if (menu || interp != currentInterp) {
|
||
[NSApp tkSetMainMenu:menu];
|
||
}
|
||
currentInterp = interp;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* CheckForSpecialMenu --
|
||
*
|
||
* Given a menu, check to see whether or not it is a cascade in a menubar
|
||
* with one of the special names ".apple", ".help" or ".window". If it is,
|
||
* the entry that points to this menu will be marked.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Will set entryFlags appropriately.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
CheckForSpecialMenu(
|
||
TkMenu *menuPtr) /* The menu we are checking */
|
||
{
|
||
if (!menuPtr->masterMenuPtr->tkwin) {
|
||
return;
|
||
}
|
||
for (TkMenuEntry *cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
|
||
cascadeEntryPtr;
|
||
cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
|
||
if (cascadeEntryPtr->menuPtr->menuType == MENUBAR
|
||
&& cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin) {
|
||
TkMenu *mainMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr;
|
||
int i = 0;
|
||
Tcl_DString ds;
|
||
|
||
Tcl_DStringInit(&ds);
|
||
Tcl_DStringAppend(&ds, Tk_PathName(mainMenuPtr->tkwin), -1);
|
||
while (specialMenus[i].name) {
|
||
Tcl_DStringAppend(&ds, specialMenus[i].name,
|
||
specialMenus[i].len);
|
||
if (strcmp(Tcl_DStringValue(&ds),
|
||
Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) {
|
||
cascadeEntryPtr->entryFlags |= specialMenus[i].flag;
|
||
} else {
|
||
cascadeEntryPtr->entryFlags &= ~specialMenus[i].flag;
|
||
}
|
||
Tcl_DStringSetLength(&ds, Tcl_DStringLength(&ds) -
|
||
specialMenus[i].len);
|
||
i++;
|
||
}
|
||
Tcl_DStringFree(&ds);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ParseAccelerator --
|
||
*
|
||
* Parse accelerator string.
|
||
*
|
||
* Results:
|
||
* Accelerator string & flags.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static NSString *
|
||
ParseAccelerator(
|
||
const char *accel,
|
||
NSUInteger *maskPtr)
|
||
{
|
||
unichar ch = 0;
|
||
size_t len;
|
||
int i;
|
||
|
||
*maskPtr = 0;
|
||
while (1) {
|
||
i = 0;
|
||
while (allModifiers[i].name) {
|
||
int l = allModifiers[i].len;
|
||
|
||
if (!strncasecmp(accel, allModifiers[i].name, l) &&
|
||
(accel[l] == '-' || accel[l] == '+')) {
|
||
*maskPtr |= allModifiers[i].mask;
|
||
accel += l+1;
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
if (!allModifiers[i].name || !*accel) {
|
||
break;
|
||
}
|
||
}
|
||
len = strlen(accel);
|
||
if (len > 1) {
|
||
i = 0;
|
||
if (accel[0] == 'F' && len < 4 && accel[1] > '0' && accel[1] <= '9') {
|
||
int fkey = accel[1] - '0';
|
||
|
||
if (len == 3) {
|
||
if (accel[2] >= '0' && accel[2] <= '9') {
|
||
fkey = 10 * fkey + (accel[2] - '0');
|
||
} else {
|
||
fkey = 0;
|
||
}
|
||
}
|
||
if (fkey >= 1 && fkey <= 15) {
|
||
ch = NSF1FunctionKey + fkey - 1;
|
||
}
|
||
} else while (specialAccelerators[i].name) {
|
||
if (accel[0] == specialAccelerators[i].name[0] &&
|
||
len == specialAccelerators[i].len && !strncasecmp(accel,
|
||
specialAccelerators[i].name, specialAccelerators[i].len)) {
|
||
ch = specialAccelerators[i].ch;
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
}
|
||
if (ch) {
|
||
return [[[NSString alloc] initWithCharacters:&ch length:1] autorelease];
|
||
} else {
|
||
return [[[[NSString alloc] initWithUTF8String:accel] autorelease]
|
||
lowercaseString];
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* ModifierCharWidth --
|
||
*
|
||
* Helper mesuring width of command char in given font.
|
||
*
|
||
* Results:
|
||
* Width of command char.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
ModifierCharWidth(
|
||
Tk_Font tkfont)
|
||
{
|
||
static NSString *cmdChar = nil;
|
||
|
||
if (!cmdChar) {
|
||
unichar cmd = kCommandUnicode;
|
||
|
||
cmdChar = [[NSString alloc] initWithCharacters:&cmd length:1];
|
||
}
|
||
return [cmdChar sizeWithAttributes:
|
||
TkMacOSXNSFontAttributesForFont(tkfont)].width;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* TkpComputeStandardMenuGeometry --
|
||
*
|
||
* This procedure is invoked to recompute the size and layout of a menu
|
||
* that is not a menubar clone.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Fields of menu entries are changed to reflect their current positions,
|
||
* and the size of the menu window itself may be changed.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpComputeStandardMenuGeometry(
|
||
TkMenu *menuPtr) /* Structure describing menu. */
|
||
{
|
||
NSSize menuSize;
|
||
Tk_Font tkfont, menuFont;
|
||
Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
|
||
int modifierCharWidth, menuModifierCharWidth;
|
||
int x, y, modifierWidth, labelWidth, indicatorSpace;
|
||
int windowWidth, windowHeight, accelWidth;
|
||
int i, maxWidth;
|
||
int entryWidth, maxIndicatorSpace, borderWidth, activeBorderWidth;
|
||
TkMenuEntry *mePtr;
|
||
int haveAccel = 0;
|
||
|
||
/*
|
||
* Do nothing if this menu is a clone.
|
||
*/
|
||
|
||
if (menuPtr->tkwin == NULL || menuPtr->masterMenuPtr != menuPtr) {
|
||
return;
|
||
}
|
||
|
||
menuSize = [(NSMenu *) menuPtr->platformData size];
|
||
Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
|
||
&borderWidth);
|
||
Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
|
||
&activeBorderWidth);
|
||
x = y = borderWidth;
|
||
windowHeight = maxWidth = 0;
|
||
maxIndicatorSpace = 0;
|
||
|
||
/*
|
||
* On the Mac especially, getting font metrics can be quite slow, so we
|
||
* want to do it intelligently. We are going to precalculate them and pass
|
||
* them down to all of the measuring and drawing routines. We will measure
|
||
* the font metrics of the menu once. If an entry does not have its own
|
||
* font set, then we give the geometry/drawing routines the menu's font and
|
||
* metrics. If an entry has its own font, we will measure that font and
|
||
* give all of the geometry/drawing the entry's font and metrics.
|
||
*/
|
||
|
||
menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
|
||
Tk_GetFontMetrics(menuFont, &menuMetrics);
|
||
menuModifierCharWidth = ModifierCharWidth(menuFont);
|
||
|
||
for (i = 0; i < (int) menuPtr->numEntries; i++) {
|
||
mePtr = menuPtr->entries[i];
|
||
if (mePtr->type == CASCADE_ENTRY || mePtr->accelLength > 0) {
|
||
haveAccel = 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < (int) menuPtr->numEntries; i++) {
|
||
mePtr = menuPtr->entries[i];
|
||
if (mePtr->type == TEAROFF_ENTRY) {
|
||
continue;
|
||
}
|
||
if (mePtr->fontPtr == NULL) {
|
||
tkfont = menuFont;
|
||
fmPtr = &menuMetrics;
|
||
modifierCharWidth = menuModifierCharWidth;
|
||
} else {
|
||
tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
|
||
Tk_GetFontMetrics(tkfont, &entryMetrics);
|
||
fmPtr = &entryMetrics;
|
||
modifierCharWidth = ModifierCharWidth(tkfont);
|
||
}
|
||
accelWidth = modifierWidth = indicatorSpace = 0;
|
||
if (mePtr->type == SEPARATOR_ENTRY) {
|
||
mePtr->height = menuSeparatorHeight;
|
||
} else {
|
||
/*
|
||
* For each entry, compute the height required by that particular
|
||
* entry, plus three widths: the width of the label, the width to
|
||
* allow for an indicator to be displayed to the left of the label
|
||
* (if any), and the width of the accelerator to be displayed to
|
||
* the right of the label (if any). These sizes depend, of course,
|
||
* on the type of the entry.
|
||
*/
|
||
|
||
NSMenuItem *menuItem = (NSMenuItem *) mePtr->platformEntryData;
|
||
int haveImage = 0, width = 0, height = 0;
|
||
|
||
if (mePtr->image) {
|
||
Tk_SizeOfImage(mePtr->image, &width, &height);
|
||
haveImage = 1;
|
||
height += 2; /* tweak */
|
||
} else if (mePtr->bitmapPtr) {
|
||
Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin,
|
||
mePtr->bitmapPtr);
|
||
|
||
Tk_SizeOfBitmap(menuPtr->display, bitmap, &width, &height);
|
||
haveImage = 1;
|
||
height += 2; /* tweak */
|
||
}
|
||
if (!haveImage || (mePtr->compound != COMPOUND_NONE)) {
|
||
NSAttributedString *attrTitle = [menuItem attributedTitle];
|
||
NSSize size;
|
||
|
||
if (attrTitle) {
|
||
size = [attrTitle size];
|
||
} else {
|
||
size = [[menuItem title] sizeWithAttributes:
|
||
TkMacOSXNSFontAttributesForFont(tkfont)];
|
||
}
|
||
size.width += menuTextLeadingEdgeMargin + menuTextTrailingEdgeMargin;
|
||
size.height -= 1; /* tweak */
|
||
if (haveImage && (mePtr->compound != COMPOUND_NONE)) {
|
||
int margin = width + menuIconTrailingEdgeMargin;
|
||
|
||
if (margin > menuTextLeadingEdgeMargin) {
|
||
margin = menuTextLeadingEdgeMargin;
|
||
}
|
||
width += size.width + menuIconTrailingEdgeMargin - margin;
|
||
if (size.height > height) {
|
||
height = size.height;
|
||
}
|
||
} else {
|
||
width = size.width;
|
||
height = size.height;
|
||
}
|
||
}
|
||
else {
|
||
/* image only. */
|
||
}
|
||
labelWidth = width + menuItemExtraWidth;
|
||
mePtr->height = height + menuItemExtraHeight;
|
||
if (mePtr->type == CASCADE_ENTRY) {
|
||
modifierWidth = modifierCharWidth;
|
||
} else if (mePtr->accelLength == 0) {
|
||
if (haveAccel && !mePtr->hideMargin) {
|
||
modifierWidth = modifierCharWidth;
|
||
}
|
||
} else {
|
||
NSUInteger modifMask = [menuItem keyEquivalentModifierMask];
|
||
int j = 0;
|
||
|
||
while (allModifiers[j].name) {
|
||
if (modifMask & allModifiers[j].mask) {
|
||
modifMask &= ~allModifiers[j].mask;
|
||
modifierWidth += modifierCharWidth;
|
||
}
|
||
j++;
|
||
}
|
||
accelWidth = [[menuItem keyEquivalent] sizeWithAttributes:
|
||
TkMacOSXNSFontAttributesForFont(tkfont)].width;
|
||
}
|
||
if (!mePtr->hideMargin) {
|
||
indicatorSpace = menuMarkColumnWidth;
|
||
}
|
||
if (indicatorSpace > maxIndicatorSpace) {
|
||
maxIndicatorSpace = indicatorSpace;
|
||
}
|
||
entryWidth = labelWidth + modifierWidth + accelWidth;
|
||
if (entryWidth > maxWidth) {
|
||
maxWidth = entryWidth;
|
||
}
|
||
menuPtr->entries[i]->width = entryWidth;
|
||
mePtr->height += 2 * activeBorderWidth;
|
||
}
|
||
mePtr->x = x;
|
||
mePtr->y = y;
|
||
y += menuPtr->entries[i]->height + borderWidth;
|
||
}
|
||
windowWidth = menuSize.width;
|
||
if (windowWidth <= 0) {
|
||
windowWidth = 1;
|
||
}
|
||
windowHeight = menuSize.height;
|
||
if (windowHeight <= 0) {
|
||
windowHeight = 1;
|
||
}
|
||
menuPtr->totalWidth = windowWidth;
|
||
menuPtr->totalHeight = windowHeight;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* GenerateMenuSelectEvent --
|
||
*
|
||
* Respond to a menu item being selected.
|
||
*
|
||
* Results:
|
||
* True if event(s) are generated - false otherwise.
|
||
*
|
||
* Side effects:
|
||
* Places a virtual event on the event queue.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
GenerateMenuSelectEvent(
|
||
TKMenu *menu,
|
||
NSMenuItem *menuItem)
|
||
{
|
||
TkMenu *menuPtr = [menu tkMenu];
|
||
|
||
if (menuPtr) {
|
||
int index = [menu tkIndexOfItem:menuItem];
|
||
|
||
if (index < 0 || index >= (int) menuPtr->numEntries ||
|
||
(menuPtr->entries[index])->state == ENTRY_DISABLED) {
|
||
TkActivateMenuEntry(menuPtr, -1);
|
||
} else {
|
||
TkActivateMenuEntry(menuPtr, index);
|
||
MenuSelectEvent(menuPtr);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* MenuSelectEvent --
|
||
*
|
||
* Generates a "MenuSelect" virtual event. This can be used to do
|
||
* context-sensitive menu help.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Places a virtual event on the event queue.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
MenuSelectEvent(
|
||
TkMenu *menuPtr) /* the menu we have selected. */
|
||
{
|
||
XVirtualEvent event;
|
||
|
||
bzero(&event, sizeof(XVirtualEvent));
|
||
event.type = VirtualEvent;
|
||
event.serial = LastKnownRequestProcessed(menuPtr->display);
|
||
event.send_event = false;
|
||
event.display = menuPtr->display;
|
||
event.event = Tk_WindowId(menuPtr->tkwin);
|
||
event.root = XRootWindow(menuPtr->display, 0);
|
||
event.subwindow = None;
|
||
event.time = TkpGetMS();
|
||
XQueryPointer(NULL, None, NULL, NULL, &event.x_root, &event.y_root, NULL,
|
||
NULL, &event.state);
|
||
event.same_screen = true;
|
||
event.name = Tk_GetUid("MenuSelect");
|
||
Tk_MakeWindowExist(menuPtr->tkwin);
|
||
if (Tcl_GetServiceMode() != TCL_SERVICE_NONE) {
|
||
Tk_HandleEvent((XEvent *) &event);
|
||
} else {
|
||
Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* RecursivelyClearActiveMenu --
|
||
*
|
||
* Recursively clears the active entry in the menu's cascade hierarchy.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Generates <<MenuSelect>> virtual events.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
RecursivelyClearActiveMenu(
|
||
TkMenu *menuPtr) /* The menu to reset. */
|
||
{
|
||
int i;
|
||
|
||
TkActivateMenuEntry(menuPtr, -1);
|
||
for (i = 0; i < (int) menuPtr->numEntries; i++) {
|
||
TkMenuEntry *mePtr = menuPtr->entries[i];
|
||
|
||
if (mePtr->type == CASCADE_ENTRY
|
||
&& (mePtr->childMenuRefPtr != NULL)
|
||
&& (mePtr->childMenuRefPtr->menuPtr != NULL)) {
|
||
RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXClearMenubarActive --
|
||
*
|
||
* Recursively clears the active entry in the current menubar hierarchy.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Generates <<MenuSelect>> virtual events.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkMacOSXClearMenubarActive(void)
|
||
{
|
||
NSMenu *mainMenu = [NSApp mainMenu];
|
||
|
||
if (mainMenu && [mainMenu isKindOfClass:[TKMenu class]]) {
|
||
TkMenu *menuPtr = [(TKMenu *) mainMenu tkMenu];
|
||
|
||
if (menuPtr &&
|
||
!(menuPtr->menuFlags & MENU_DELETION_PENDING) &&
|
||
menuPtr->numEntries > 0 &&
|
||
menuPtr->entries != NULL) {
|
||
RecursivelyClearActiveMenu(menuPtr);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* Tk_MacOSXTurnOffMenus --
|
||
*
|
||
* Turns off all the menu drawing code. This is more than just disabling
|
||
* the "menu" command, this means that Tk will NEVER touch the menubar.
|
||
* It is needed in the Plugin, where Tk does not own the menubar.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* A flag is set which will disable all menu drawing.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
Tk_MacOSXTurnOffMenus(void)
|
||
{
|
||
gNoTkMenus = 1;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpMenuInit --
|
||
*
|
||
* Initializes Mac-specific menu data.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Allocates a hash table.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpMenuInit(void)
|
||
{
|
||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||
|
||
#define observe(n, s) \
|
||
[nc addObserver:NSApp selector:@selector(s) name:(n) object:nil]
|
||
observe(NSMenuDidBeginTrackingNotification, menuBeginTracking:);
|
||
observe(NSMenuDidEndTrackingNotification, menuEndTracking:);
|
||
#undef observe
|
||
|
||
[NSMenuItem setUsesUserKeyEquivalents:NO];
|
||
ChkErr(GetThemeMetric, kThemeMetricMenuMarkColumnWidth,
|
||
&menuMarkColumnWidth);
|
||
ChkErr(GetThemeMetric, kThemeMetricMenuTextLeadingEdgeMargin,
|
||
&menuTextLeadingEdgeMargin);
|
||
ChkErr(GetThemeMetric, kThemeMetricMenuTextTrailingEdgeMargin,
|
||
&menuTextTrailingEdgeMargin);
|
||
ChkErr(GetThemeMetric, kThemeMetricMenuIconTrailingEdgeMargin,
|
||
&menuIconTrailingEdgeMargin);
|
||
ChkErr(GetThemeMenuItemExtra, kThemeMenuItemPlain, &menuItemExtraHeight,
|
||
&menuItemExtraWidth);
|
||
ChkErr(GetThemeMenuSeparatorHeight, &menuSeparatorHeight);
|
||
}
|
||
|
||
#pragma mark -
|
||
#pragma mark NOPs
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpMenuThreadInit --
|
||
*
|
||
* Does platform-specific initialization of thread-specific menu state.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpMenuThreadInit(void)
|
||
{
|
||
/*
|
||
* Nothing to do.
|
||
*/
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpMenuNotifyToplevelCreate --
|
||
*
|
||
* This routine reconfigures the menu and the clones indicated by
|
||
* menuName because a toplevel has been created and any system menus need
|
||
* to be created. Only applicable to Windows.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* An idle handler is set up to do the reconfiguration.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpMenuNotifyToplevelCreate(
|
||
TCL_UNUSED(Tcl_Interp *), /* The interp the menu lives in. */
|
||
TCL_UNUSED(const char *)) /* The name of the menu to reconfigure. */
|
||
{
|
||
/*
|
||
* Nothing to do.
|
||
*/
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* TkpInitializeMenuBindings --
|
||
*
|
||
* For every interp, initializes the bindings for Windows menus. Does
|
||
* nothing on Mac or XWindows.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* C-level bindings are setup for the interp which will handle Alt-key
|
||
* sequences for menus without beeping or interfering with user-defined
|
||
* Alt-key bindings.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpInitializeMenuBindings(
|
||
TCL_UNUSED(Tcl_Interp *), /* The interpreter to set. */
|
||
TCL_UNUSED(Tk_BindingTable))
|
||
/* The table to add to. */
|
||
{
|
||
/*
|
||
* Nothing to do.
|
||
*/
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* TkpComputeMenubarGeometry --
|
||
*
|
||
* This procedure is invoked to recompute the size and layout of a menu
|
||
* that is a menubar clone.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Fields of menu entries are changed to reflect their current positions,
|
||
* and the size of the menu window itself may be changed.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpComputeMenubarGeometry(
|
||
TkMenu *menuPtr) /* Structure describing menu. */
|
||
{
|
||
TkpComputeStandardMenuGeometry(menuPtr);
|
||
}
|
||
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkpDrawMenuEntry --
|
||
*
|
||
* Draws the given menu entry at the given coordinates with the given
|
||
* attributes. This is a no-op on macOS since the menus are drawn by
|
||
* the Apple window manager, which also handles all events related to
|
||
* selecting menu items. This function is only called for tearoff
|
||
* menus, which are not supported on macOS but do get drawn as nearly
|
||
* invisible 1 pixel wide windows on macOS
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* None
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkpDrawMenuEntry(
|
||
TCL_UNUSED(TkMenuEntry *), /* The entry to draw */
|
||
TCL_UNUSED(Drawable), /* What to draw into */
|
||
TCL_UNUSED(Tk_Font), /* Precalculated font for menu */
|
||
TCL_UNUSED(const Tk_FontMetrics *),
|
||
/* Precalculated metrics for menu */
|
||
TCL_UNUSED(int), /* X-coordinate of topleft of entry */
|
||
TCL_UNUSED(int), /* Y-coordinate of topleft of entry */
|
||
TCL_UNUSED(int), /* Width of the entry rectangle */
|
||
TCL_UNUSED(int), /* Height of the current rectangle */
|
||
TCL_UNUSED(int), /* Boolean flag */
|
||
TCL_UNUSED(int)) /* Whether or not to draw the cascade arrow
|
||
* for cascade items. */
|
||
{
|
||
}
|
||
|
||
#pragma mark Obsolete
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXPreprocessMenu --
|
||
*
|
||
* Handle preprocessing of menubar if it exists.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* All post commands for the current menubar get executed.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkMacOSXPreprocessMenu(void)
|
||
{
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXUseID --
|
||
*
|
||
* Take the ID out of the available list for new menus. Used by the
|
||
* default menu bar's menus so that they do not get created at the Tk
|
||
* level. See TkMacOSXGetNewMenuID for more information.
|
||
*
|
||
* Results:
|
||
* Returns TCL_OK if the id was not in use. Returns TCL_ERROR if the id
|
||
* was in use.
|
||
*
|
||
* Side effects:
|
||
* A hash table entry in the command table is created with a NULL value.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkMacOSXUseMenuID(
|
||
TCL_UNUSED(short)) /* The id to take out of the table */
|
||
{
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXDispatchMenuEvent --
|
||
*
|
||
* Given a menu id and an item, dispatches the command associated with it.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Commands for the event are scheduled for execution at idle time.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
TkMacOSXDispatchMenuEvent(
|
||
TCL_UNUSED(int), /* The menu id of the menu we are invoking */
|
||
TCL_UNUSED(int)) /* The one-based index of the item that was
|
||
* selected. */
|
||
{
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXHandleTearoffMenu() --
|
||
*
|
||
* This routine sees if the MDEF has set a menu and a mouse position for
|
||
* tearing off and makes a tearoff menu if it has.
|
||
*
|
||
* Results:
|
||
* menuPtr->interp will have the result of the tearoff command.
|
||
*
|
||
* Side effects:
|
||
* A new tearoff menu is created if it is supposed to be.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkMacOSXHandleTearoffMenu(void)
|
||
{
|
||
/*
|
||
* Obsolete: Nothing to do.
|
||
*/
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXSetHelpMenuItemCount --
|
||
*
|
||
* Has to be called after the first call to InsertMenu. Sets up the global
|
||
* variable for the number of items in the unmodified help menu.
|
||
*
|
||
* NB: Nobody uses this any more, since you can get the number of system
|
||
* help items from HMGetHelpMenu trivially. But it is in the stubs
|
||
* table...
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Nothing.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkMacOSXSetHelpMenuItemCount(void)
|
||
{
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* TkMacOSXMenuClick --
|
||
*
|
||
* Prepares a menubar for MenuSelect or MenuKey.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Any pending configurations of the menubar are completed.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkMacOSXMenuClick(void)
|
||
{
|
||
}
|
||
|
||
/*
|
||
* Local Variables:
|
||
* mode: objc
|
||
* c-basic-offset: 4
|
||
* fill-column: 79
|
||
* coding: utf-8
|
||
* End:
|
||
*/
|