Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m
41152 views
/*1* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <Carbon/Carbon.h>26#import "CMenuItem.h"27#import "CMenu.h"28#import "AWTEvent.h"29#import "AWTWindow.h"30#import "ThreadUtilities.h"31#import "JNIUtilities.h"3233#import "java_awt_Event.h"34#import "java_awt_event_KeyEvent.h"35#import "sun_lwawt_macosx_CMenuItem.h"3637#define NOT_A_CHECKBOXMENU -2383940@implementation CMenuItem4142- (id) initWithPeer:(jobject)peer asSeparator: (BOOL) asSeparator{43AWT_ASSERT_APPKIT_THREAD;44self = [super initWithPeer:peer];45if (self) {46if (asSeparator) {47fMenuItem = (NSMenuItem*)[NSMenuItem separatorItem];48[fMenuItem retain];49} else {50fMenuItem = [[NSMenuItem alloc] init];51[fMenuItem setAction:@selector(handleAction:)];52[fMenuItem setTarget:self];53}54fIsCheckbox = NO;55fIsEnabled = YES;56}57return self;58}5960// This is because NSApplication doesn't check the target's window when sending61// actions; they only check the target itself. We always return YES,62// since we shouldn't even be installed unless our window is active.63- (BOOL) worksWhenModal {64return YES;65}6667// Events68- (void)handleAction:(NSMenuItem *)sender {69AWT_ASSERT_APPKIT_THREAD;70JNIEnv *env = [ThreadUtilities getJNIEnv];71JNI_COCOA_ENTER(env);7273// If we are called as a result of user pressing a shortcut, do nothing,74// because AVTView has already sent corresponding key event to the Java75// layer from performKeyEquivalent.76// There is an exception from the rule above, though: if a window with77// a menu gets minimized by user and there are no other windows to take78// focus, the window's menu won't be removed from the global menu bar.79// However, the Java layer won't handle invocation by a shortcut coming80// from this "frameless" menu, because there are no active windows. This81// means we have to handle it here.82NSEvent *currEvent = [[NSApplication sharedApplication] currentEvent];8384if ([currEvent type] == NSKeyDown) {85// The action event can be ignored only if the key window is an AWT window.86// Otherwise, the action event is the only notification and must be processed.87NSWindow *keyWindow = [NSApp keyWindow];88if (keyWindow != nil && [AWTWindow isAWTWindow: keyWindow]) {89return;90}91}9293if (fIsCheckbox) {94DECLARE_CLASS(jc_CCheckboxMenuItem, "sun/lwawt/macosx/CCheckboxMenuItem");95DECLARE_METHOD(jm_ckHandleAction, jc_CCheckboxMenuItem, "handleAction", "(Z)V");9697// Send the opposite of what's currently checked -- the action98// indicates what state we're going to.99NSInteger state = [sender state];100jboolean newState = (state == NSOnState ? JNI_FALSE : JNI_TRUE);101(*env)->CallVoidMethod(env, fPeer, jm_ckHandleAction, newState);102} else {103DECLARE_CLASS(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem");104DECLARE_METHOD(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event)105106NSUInteger modifiers = [currEvent modifierFlags];107jint javaModifiers = NsKeyModifiersToJavaModifiers(modifiers, NO);108109(*env)->CallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)110}111CHECK_EXCEPTION();112JNI_COCOA_EXIT(env);113}114115- (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers {116117NSUInteger modifierMask = 0;118119if (![theKeyEquivalent isEqualToString:@""]) {120// Force the key equivalent to lower case if not using the shift key.121// Otherwise AppKit will draw a Shift glyph in the menu.122if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) == 0) {123theKeyEquivalent = [theKeyEquivalent lowercaseString];124}125126// Hack for the question mark -- SHIFT and / means use the question mark.127if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) != 0 &&128[theKeyEquivalent isEqualToString:@"/"])129{130theKeyEquivalent = @"?";131modifiers &= ~java_awt_event_KeyEvent_SHIFT_MASK;132}133134modifierMask = JavaModifiersToNsKeyModifiers(modifiers, NO);135}136137[ThreadUtilities performOnMainThreadWaiting:YES block:^(){138[fMenuItem setKeyEquivalent:theKeyEquivalent];139[fMenuItem setKeyEquivalentModifierMask:modifierMask];140[fMenuItem setTitle:theLabel];141}];142}143144- (void) setJavaImage:(NSImage *)theImage {145146[ThreadUtilities performOnMainThreadWaiting:NO block:^(){147[fMenuItem setImage:theImage];148}];149}150151- (void) setJavaToolTipText:(NSString *)theText {152153[ThreadUtilities performOnMainThreadWaiting:NO block:^(){154[fMenuItem setToolTip:theText];155}];156}157158159- (void)setJavaEnabled:(BOOL) enabled {160161[ThreadUtilities performOnMainThreadWaiting:NO block:^(){162@synchronized(self) {163fIsEnabled = enabled;164165// Warning: This won't work if the parent menu is disabled.166// See [CMenu syncFromJava]. We still need to call it here so167// the NSMenuItem itself gets properly updated.168[fMenuItem setEnabled:fIsEnabled];169}170}];171}172173- (BOOL)isEnabled {174175BOOL enabled = NO;176@synchronized(self) {177enabled = fIsEnabled;178}179return enabled;180}181182183- (void)setJavaState:(BOOL)newState {184185[ThreadUtilities performOnMainThreadWaiting:NO block:^(){186[fMenuItem setState:(newState ? NSOnState : NSOffState)];187}];188}189190- (void)dealloc {191[fMenuItem setAction:NULL];192[fMenuItem setTarget:nil];193[fMenuItem release];194fMenuItem = nil;195196[super dealloc];197}198199- (void)addNSMenuItemToMenu:(NSMenu *)inMenu {200[inMenu addItem:fMenuItem];201}202203- (NSMenuItem *)menuItem {204return [[fMenuItem retain] autorelease];205}206207- (void)setIsCheckbox {208fIsCheckbox = YES;209}210211- (NSString *)description {212return [NSString stringWithFormat:@"CMenuItem[ %@ ]", fMenuItem];213}214215@end216217/** Convert a Java keycode for SetMenuItemCmd */218static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) {219unichar macKey = 0;220221if ((awtKey >= java_awt_event_KeyEvent_VK_0 && awtKey <= java_awt_event_KeyEvent_VK_9) ||222(awtKey >= java_awt_event_KeyEvent_VK_A && awtKey <= java_awt_event_KeyEvent_VK_Z))223{224// These ranges are the same in ASCII225macKey = awtKey;226} else if (awtKey >= java_awt_event_KeyEvent_VK_F1 && awtKey <= java_awt_event_KeyEvent_VK_F12) {227// Support for F1 - F12 has been around since Java 1.0 and fall into a lower range.228macKey = awtKey - java_awt_event_KeyEvent_VK_F1 + NSF1FunctionKey;229} else if (awtKey >= java_awt_event_KeyEvent_VK_F13 && awtKey <= java_awt_event_KeyEvent_VK_F24) {230// Support for F13-F24 came in Java 1.2 and are at a different range.231macKey = awtKey - java_awt_event_KeyEvent_VK_F13 + NSF13FunctionKey;232} else {233// Special characters234switch (awtKey) {235case java_awt_event_KeyEvent_VK_BACK_QUOTE : macKey = '`'; break;236case java_awt_event_KeyEvent_VK_QUOTE : macKey = '\''; break;237238case java_awt_event_KeyEvent_VK_ESCAPE : macKey = 0x1B; break;239case java_awt_event_KeyEvent_VK_SPACE : macKey = ' '; break;240case java_awt_event_KeyEvent_VK_PAGE_UP : macKey = NSPageUpFunctionKey; break;241case java_awt_event_KeyEvent_VK_PAGE_DOWN : macKey = NSPageDownFunctionKey; break;242case java_awt_event_KeyEvent_VK_END : macKey = NSEndFunctionKey; break;243case java_awt_event_KeyEvent_VK_HOME : macKey = NSHomeFunctionKey; break;244245case java_awt_event_KeyEvent_VK_LEFT : macKey = NSLeftArrowFunctionKey; break;246case java_awt_event_KeyEvent_VK_UP : macKey = NSUpArrowFunctionKey; break;247case java_awt_event_KeyEvent_VK_RIGHT : macKey = NSRightArrowFunctionKey; break;248case java_awt_event_KeyEvent_VK_DOWN : macKey = NSDownArrowFunctionKey; break;249250case java_awt_event_KeyEvent_VK_COMMA : macKey = ','; break;251252// Mac OS doesn't distinguish between the two '-' keys...253case java_awt_event_KeyEvent_VK_MINUS :254case java_awt_event_KeyEvent_VK_SUBTRACT : macKey = '-'; break;255256// or the two '.' keys...257case java_awt_event_KeyEvent_VK_DECIMAL :258case java_awt_event_KeyEvent_VK_PERIOD : macKey = '.'; break;259260// or the two '/' keys.261case java_awt_event_KeyEvent_VK_DIVIDE :262case java_awt_event_KeyEvent_VK_SLASH : macKey = '/'; break;263264case java_awt_event_KeyEvent_VK_SEMICOLON : macKey = ';'; break;265case java_awt_event_KeyEvent_VK_EQUALS : macKey = '='; break;266267case java_awt_event_KeyEvent_VK_OPEN_BRACKET : macKey = '['; break;268case java_awt_event_KeyEvent_VK_BACK_SLASH : macKey = '\\'; break;269case java_awt_event_KeyEvent_VK_CLOSE_BRACKET : macKey = ']'; break;270271case java_awt_event_KeyEvent_VK_MULTIPLY : macKey = '*'; break;272case java_awt_event_KeyEvent_VK_ADD : macKey = '+'; break;273274case java_awt_event_KeyEvent_VK_HELP : macKey = NSHelpFunctionKey; break;275case java_awt_event_KeyEvent_VK_TAB : macKey = NSTabCharacter; break;276case java_awt_event_KeyEvent_VK_ENTER : macKey = NSNewlineCharacter; break;277case java_awt_event_KeyEvent_VK_BACK_SPACE : macKey = NSBackspaceCharacter; break;278case java_awt_event_KeyEvent_VK_DELETE : macKey = NSDeleteCharacter; break;279case java_awt_event_KeyEvent_VK_CLEAR : macKey = NSClearDisplayFunctionKey; break;280case java_awt_event_KeyEvent_VK_AMPERSAND : macKey = '&'; break;281case java_awt_event_KeyEvent_VK_ASTERISK : macKey = '*'; break;282case java_awt_event_KeyEvent_VK_QUOTEDBL : macKey = '\"'; break;283case java_awt_event_KeyEvent_VK_LESS : macKey = '<'; break;284case java_awt_event_KeyEvent_VK_GREATER : macKey = '>'; break;285case java_awt_event_KeyEvent_VK_BRACELEFT : macKey = '{'; break;286case java_awt_event_KeyEvent_VK_BRACERIGHT : macKey = '}'; break;287case java_awt_event_KeyEvent_VK_AT : macKey = '@'; break;288case java_awt_event_KeyEvent_VK_COLON : macKey = ':'; break;289case java_awt_event_KeyEvent_VK_CIRCUMFLEX : macKey = '^'; break;290case java_awt_event_KeyEvent_VK_DOLLAR : macKey = '$'; break;291case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break;292case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break;293case java_awt_event_KeyEvent_VK_NUMBER_SIGN : macKey = '#'; break;294case java_awt_event_KeyEvent_VK_PLUS : macKey = '+'; break;295case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break;296case java_awt_event_KeyEvent_VK_UNDERSCORE : macKey = '_'; break;297}298}299return macKey;300}301302/*303* Class: sun_lwawt_macosx_CMenuItem304* Method: nativeSetLabel305* Signature: (JLjava/lang/String;CII)V306*/307JNIEXPORT void JNICALL308Java_sun_lwawt_macosx_CMenuItem_nativeSetLabel309(JNIEnv *env, jobject peer,310jlong menuItemObj, jstring label,311jchar shortcutKey, jint shortcutKeyCode, jint mods)312{313JNI_COCOA_ENTER(env);314NSString *theLabel = JavaStringToNSString(env, label);315NSString *theKeyEquivalent = nil;316unichar macKey = shortcutKey;317318if (macKey == 0) {319macKey = AWTKeyToMacShortcut(shortcutKeyCode, (mods & java_awt_event_KeyEvent_SHIFT_MASK) != 0);320}321322if (macKey != 0) {323unichar equivalent[1] = {macKey};324theKeyEquivalent = [NSString stringWithCharacters:equivalent length:1];325} else {326theKeyEquivalent = @"";327}328329[((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaLabel:theLabel shortcut:theKeyEquivalent modifierMask:mods];330JNI_COCOA_EXIT(env);331}332333/*334* Class: sun_lwawt_macosx_CMenuItem335* Method: nativeSetTooltip336* Signature: (JLjava/lang/String;)V337*/338JNIEXPORT void JNICALL339Java_sun_lwawt_macosx_CMenuItem_nativeSetTooltip340(JNIEnv *env, jobject peer, jlong menuItemObj, jstring tooltip)341{342JNI_COCOA_ENTER(env);343NSString *theTooltip = JavaStringToNSString(env, tooltip);344[((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaToolTipText:theTooltip];345JNI_COCOA_EXIT(env);346}347348/*349* Class: sun_lwawt_macosx_CMenuItem350* Method: nativeSetImage351* Signature: (JJ)V352*/353JNIEXPORT void JNICALL354Java_sun_lwawt_macosx_CMenuItem_nativeSetImage355(JNIEnv *env, jobject peer, jlong menuItemObj, jlong image)356{357JNI_COCOA_ENTER(env);358[((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaImage:(NSImage*)jlong_to_ptr(image)];359JNI_COCOA_EXIT(env);360}361362/*363* Class: sun_lwawt_macosx_CMenuItem364* Method: nativeCreate365* Signature: (JZ)J366*/367JNIEXPORT jlong JNICALL368Java_sun_lwawt_macosx_CMenuItem_nativeCreate369(JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator)370{371372__block CMenuItem *aCMenuItem = nil;373BOOL asSeparator = (isSeparator == JNI_TRUE) ? YES: NO;374CMenu *parentCMenu = (CMenu *)jlong_to_ptr(parentCMenuObj);375JNI_COCOA_ENTER(env);376377jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer);378379[ThreadUtilities performOnMainThreadWaiting:YES block:^(){380aCMenuItem = [[CMenuItem alloc] initWithPeer: cPeerObjGlobal381asSeparator: asSeparator];382// the CMenuItem is released in CMenuComponent.dispose()383}];384385if (aCMenuItem == nil) {386return 0L;387}388389// and add it to the parent item.390[parentCMenu addJavaMenuItem: aCMenuItem];391392// setLabel will be called after creation completes.393394JNI_COCOA_EXIT(env);395return ptr_to_jlong(aCMenuItem);396}397398/*399* Class: sun_lwawt_macosx_CMenuItem400* Method: nativeSetEnabled401* Signature: (JZ)V402*/403JNIEXPORT void JNICALL404Java_sun_lwawt_macosx_CMenuItem_nativeSetEnabled405(JNIEnv *env, jobject peer, jlong menuItemObj, jboolean enable)406{407JNI_COCOA_ENTER(env);408CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);409[item setJavaEnabled: (enable == JNI_TRUE)];410JNI_COCOA_EXIT(env);411}412413/*414* Class: sun_lwawt_macosx_CCheckboxMenuItem415* Method: nativeSetState416* Signature: (IZ)V417*/418JNIEXPORT void JNICALL419Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetState420(JNIEnv *env, jobject peer, jlong menuItemObj, jboolean state)421{422JNI_COCOA_ENTER(env);423CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);424[item setJavaState: (state == JNI_TRUE)];425JNI_COCOA_EXIT(env);426}427428/*429* Class: sun_lwawt_macosx_CCheckboxMenuItem430* Method: nativeSetState431* Signature: (IZ)V432*/433JNIEXPORT void JNICALL434Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetIsCheckbox435(JNIEnv *env, jobject peer, jlong menuItemObj)436{437JNI_COCOA_ENTER(env);438CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);439[item setIsCheckbox];440JNI_COCOA_EXIT(env);441}442443444