jdk/src/macosx/native/sun/awt/CMenuItem.m
changeset 12047 320a714614e9
child 12283 253d9a09f674
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/macosx/native/sun/awt/CMenuItem.m	Tue Mar 06 20:34:38 2012 +0000
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#import <JavaNativeFoundation/JavaNativeFoundation.h>
+
+#import "CMenuItem.h"
+#import "CMenu.h"
+#import "AWTEvent.h"
+#import "ThreadUtilities.h"
+
+#import "java_awt_Event.h"
+#import "java_awt_event_KeyEvent.h"
+#import "sun_lwawt_macosx_CMenuItem.h"
+
+#define NOT_A_CHECKBOXMENU -2
+
+
+@implementation CMenuItem
+
+- (id) initWithPeer:(jobject)peer asSeparator: (NSNumber *) asSeparator{
+AWT_ASSERT_APPKIT_THREAD;
+    self = [super initWithPeer:peer];
+    if (self) {
+        if ([asSeparator boolValue]) {
+            fMenuItem = (NSMenuItem*)[NSMenuItem separatorItem];
+            [fMenuItem retain];
+        } else {
+            fMenuItem = [[NSMenuItem alloc] init];
+            [fMenuItem setAction:@selector(handleAction:)];
+            [fMenuItem setTarget:self];
+        }
+        fIsCheckbox = NO;
+        fIsEnabled = YES;
+    }
+    return self;
+}
+
+// This is because NSApplication doesn't check the target's window when sending
+// actions; they only check the target itself.  We always return YES,
+// since we shouldn't even be installed unless our window is active.
+- (BOOL) worksWhenModal {
+    return YES;
+}
+
+// Events
+- (void)handleAction:(NSMenuItem *)sender {
+AWT_ASSERT_APPKIT_THREAD;
+    JNIEnv *env = [ThreadUtilities getJNIEnv];
+JNF_COCOA_ENTER(env);
+
+    if (fIsCheckbox) {
+        static JNF_CLASS_CACHE(jc_CCheckboxMenuItem, "sun/lwawt/macosx/CCheckboxMenuItem");
+        static JNF_MEMBER_CACHE(jm_ckHandleAction, jc_CCheckboxMenuItem, "handleAction", "(Z)V");
+
+        // Send the opposite of what's currently checked -- the action
+        // indicates what state we're going to.
+        NSInteger state = [sender state];
+        jboolean newState = (state == NSOnState ? JNI_FALSE : JNI_TRUE);
+        JNFCallVoidMethod(env, fPeer, jm_ckHandleAction, newState);
+    } else {
+        static JNF_CLASS_CACHE(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem");
+        static JNF_MEMBER_CACHE(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event)
+
+        NSEvent *currEvent = [[NSApplication sharedApplication] currentEvent];
+        NSUInteger modifiers = [currEvent modifierFlags];
+        jint javaModifiers = 0;
+
+        if ((modifiers & NSCommandKeyMask) != 0)   javaModifiers |= java_awt_Event_META_MASK;
+        if ((modifiers & NSShiftKeyMask) != 0)     javaModifiers |= java_awt_Event_SHIFT_MASK;
+        if ((modifiers & NSControlKeyMask) != 0)   javaModifiers |= java_awt_Event_CTRL_MASK;
+        if ((modifiers & NSAlternateKeyMask) != 0) javaModifiers |= java_awt_Event_ALT_MASK;
+
+        JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)
+    }
+JNF_COCOA_EXIT(env);
+}
+
+- (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers {
+AWT_ASSERT_NOT_APPKIT_THREAD;
+
+    NSUInteger modifierMask = 0;
+
+    if (![theKeyEquivalent isEqualToString:@""]) {
+        // Force the key equivalent to lower case if not using the shift key.
+        // Otherwise AppKit will draw a Shift glyph in the menu.
+        if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) == 0) {
+            theKeyEquivalent = [theKeyEquivalent lowercaseString];
+        }
+
+        // Hack for the question mark -- SHIFT and / means use the question mark.
+        if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) != 0 &&
+            [theKeyEquivalent isEqualToString:@"/"])
+        {
+            theKeyEquivalent = @"?";
+            modifiers &= ~java_awt_event_KeyEvent_SHIFT_MASK;
+        }
+
+        if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) != 0) modifierMask |= NSShiftKeyMask;
+        if ((modifiers & java_awt_event_KeyEvent_CTRL_MASK) != 0)  modifierMask |= NSControlKeyMask;
+        if ((modifiers & java_awt_event_KeyEvent_ALT_MASK) != 0)   modifierMask |= NSAlternateKeyMask;
+        if ((modifiers & java_awt_event_KeyEvent_META_MASK) != 0)  modifierMask |= NSCommandKeyMask;
+    }
+
+    [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
+        AWT_ASSERT_APPKIT_THREAD;
+
+        if (![theKeyEquivalent isEqualToString:@""]) {
+            [fMenuItem setKeyEquivalent:theKeyEquivalent];
+            [fMenuItem setKeyEquivalentModifierMask:modifierMask];
+        }
+        [fMenuItem setTitle:theLabel];
+    }];
+}
+
+- (void) setJavaImage:(NSImage *)theImage {
+AWT_ASSERT_NOT_APPKIT_THREAD;
+
+    [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+        AWT_ASSERT_APPKIT_THREAD;
+
+        [fMenuItem setImage:theImage];
+    }];
+}
+
+- (void) setJavaToolTipText:(NSString *)theText {
+AWT_ASSERT_NOT_APPKIT_THREAD;
+
+    [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+        AWT_ASSERT_APPKIT_THREAD;
+
+        [fMenuItem setToolTip:theText];
+    }];
+}
+
+
+- (void)setJavaEnabled:(BOOL) enabled {
+AWT_ASSERT_NOT_APPKIT_THREAD;
+
+    [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+        AWT_ASSERT_APPKIT_THREAD;
+
+        @synchronized(self) {
+            fIsEnabled = enabled;
+
+            // Warning:  This won't work if the parent menu is disabled.
+            // See [CMenu syncFromJava]. We still need to call it here so
+            // the NSMenuItem itself gets properly updated.
+            [fMenuItem setEnabled:fIsEnabled];
+        }
+    }];
+}
+
+- (BOOL)isEnabled {
+    // AWT_ASSERT_ANY_THREAD;
+
+    BOOL enabled = NO;
+    @synchronized(self) {
+        enabled = fIsEnabled;
+    }
+    return enabled;
+}
+
+
+- (void)setJavaState:(BOOL)newState {
+AWT_ASSERT_NOT_APPKIT_THREAD;
+
+    [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+AWT_ASSERT_APPKIT_THREAD;
+
+        [fMenuItem setState:(newState ? NSOnState : NSOffState)];
+    }];
+}
+
+- (void)cleanup {
+    [fMenuItem setAction:NULL];
+    [fMenuItem setTarget:nil];
+}
+
+- (void)dealloc {
+    [fMenuItem release];
+    fMenuItem = nil;
+
+    [super dealloc];
+}
+
+- (void)addNSMenuItemToMenu:(NSMenu *)inMenu {
+    [inMenu addItem:fMenuItem];
+}
+
+- (NSMenuItem *)menuItem {
+    return [[fMenuItem retain] autorelease];
+}
+
+- (void)setIsCheckbox {
+    fIsCheckbox = YES;
+}
+
+- (void) _createMenuItem_OnAppKitThread: (NSMutableArray *)argValue {
+    jobject cPeerObjGlobal = (jobject)[[argValue objectAtIndex: 0] pointerValue];
+    NSNumber * asSeparator = (NSNumber *)[argValue objectAtIndex: 1];
+    CMenuItem *aCMenuItem = [self initWithPeer: cPeerObjGlobal asSeparator: asSeparator];
+    [argValue removeAllObjects];
+    [argValue addObject: aCMenuItem];
+}
+
+- (NSString *)description {
+    return [NSString stringWithFormat:@"CMenuItem[ %@ ]", fMenuItem];
+}
+
+@end
+
+/** Convert a Java keycode for SetMenuItemCmd */
+static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) {
+    unichar macKey = 0;
+
+    if ((awtKey >= java_awt_event_KeyEvent_VK_0 && awtKey <= java_awt_event_KeyEvent_VK_9) ||
+        (awtKey >= java_awt_event_KeyEvent_VK_A && awtKey <= java_awt_event_KeyEvent_VK_Z))
+    {
+        // These ranges are the same in ASCII
+        macKey = awtKey;
+    } else if (awtKey >= java_awt_event_KeyEvent_VK_F1 && awtKey <= java_awt_event_KeyEvent_VK_F12) {
+        // Support for F1 - F12 has been around since Java 1.0 and fall into a lower range.
+        macKey = awtKey - java_awt_event_KeyEvent_VK_F1 + NSF1FunctionKey;
+    } else if (awtKey >= java_awt_event_KeyEvent_VK_F13 && awtKey <= java_awt_event_KeyEvent_VK_F24) {
+        // Support for F13-F24 came in Java 1.2 and are at a different range.
+        macKey = awtKey - java_awt_event_KeyEvent_VK_F13 + NSF13FunctionKey;
+    } else {
+        // Special characters
+        switch (awtKey) {
+        case java_awt_event_KeyEvent_VK_BACK_QUOTE      : macKey = '`'; break;
+        case java_awt_event_KeyEvent_VK_QUOTE           : macKey = '\''; break;
+
+        case java_awt_event_KeyEvent_VK_ESCAPE          : macKey = 0x1B; break;
+//        case java_awt_event_KeyEvent_VK_SPACE           : macKey = kMenuSpaceGlyph; break;
+        case java_awt_event_KeyEvent_VK_PAGE_UP         : macKey = NSPageUpFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_PAGE_DOWN       : macKey = NSPageDownFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_END             : macKey = NSEndFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_HOME            : macKey = NSHomeFunctionKey; break;
+
+        case java_awt_event_KeyEvent_VK_LEFT            : macKey = NSLeftArrowFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_UP              : macKey = NSUpArrowFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_RIGHT           : macKey = NSRightArrowFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_DOWN            : macKey = NSDownArrowFunctionKey; break;
+
+        case java_awt_event_KeyEvent_VK_COMMA           : macKey = ','; break;
+
+        // Mac OS doesn't distinguish between the two '-' keys...
+        case java_awt_event_KeyEvent_VK_MINUS           :
+        case java_awt_event_KeyEvent_VK_SUBTRACT        : macKey = '-'; break;
+
+        // or the two '.' keys...
+        case java_awt_event_KeyEvent_VK_DECIMAL         :
+        case java_awt_event_KeyEvent_VK_PERIOD          : macKey = '.'; break;
+
+        // or the two '/' keys.
+        case java_awt_event_KeyEvent_VK_DIVIDE          :
+        case java_awt_event_KeyEvent_VK_SLASH           : macKey = '/'; break;
+
+        case java_awt_event_KeyEvent_VK_SEMICOLON       : macKey = ';'; break;
+        case java_awt_event_KeyEvent_VK_EQUALS          : macKey = '='; break;
+
+        case java_awt_event_KeyEvent_VK_OPEN_BRACKET    : macKey = '['; break;
+        case java_awt_event_KeyEvent_VK_BACK_SLASH      : macKey = '\\'; break;
+        case java_awt_event_KeyEvent_VK_CLOSE_BRACKET   : macKey = ']'; break;
+
+        case java_awt_event_KeyEvent_VK_MULTIPLY        : macKey = '*'; break;
+        case java_awt_event_KeyEvent_VK_ADD             : macKey = '+'; break;
+
+        case java_awt_event_KeyEvent_VK_HELP            : macKey = NSHelpFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_TAB             : macKey = NSTabCharacter; break;
+        case java_awt_event_KeyEvent_VK_ENTER           : macKey = NSCarriageReturnCharacter; break;
+        case java_awt_event_KeyEvent_VK_BACK_SPACE      : macKey = NSBackspaceCharacter; break;
+        case java_awt_event_KeyEvent_VK_DELETE          : macKey = NSDeleteCharacter; break;
+        case java_awt_event_KeyEvent_VK_CLEAR           : macKey = NSClearDisplayFunctionKey; break;
+        case java_awt_event_KeyEvent_VK_AMPERSAND       : macKey = '&'; break;
+        case java_awt_event_KeyEvent_VK_ASTERISK        : macKey = '*'; break;
+        case java_awt_event_KeyEvent_VK_QUOTEDBL        : macKey = '\"'; break;
+        case java_awt_event_KeyEvent_VK_LESS            : macKey = '<'; break;
+        case java_awt_event_KeyEvent_VK_GREATER         : macKey = '>'; break;
+        case java_awt_event_KeyEvent_VK_BRACELEFT       : macKey = '{'; break;
+        case java_awt_event_KeyEvent_VK_BRACERIGHT      : macKey = '}'; break;
+        case java_awt_event_KeyEvent_VK_AT              : macKey = '@'; break;
+        case java_awt_event_KeyEvent_VK_COLON           : macKey = ':'; break;
+        case java_awt_event_KeyEvent_VK_CIRCUMFLEX      : macKey = '^'; break;
+        case java_awt_event_KeyEvent_VK_DOLLAR          : macKey = '$'; break;
+        case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break;
+        case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break;
+        case java_awt_event_KeyEvent_VK_NUMBER_SIGN     : macKey = '#'; break;
+        case java_awt_event_KeyEvent_VK_PLUS            : macKey = '+'; break;
+        case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break;
+        case java_awt_event_KeyEvent_VK_UNDERSCORE      : macKey = '_'; break;
+        }
+    }
+    return macKey;
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CMenuItem
+ * Method:    nativeSetLabel
+ * Signature: (JLjava/lang/String;CII)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CMenuItem_nativeSetLabel
+(JNIEnv *env, jobject peer,
+     jlong menuItemObj, jstring label,
+     jchar shortcutKey, jint shortcutKeyCode, jint mods)
+{
+JNF_COCOA_ENTER(env);
+    NSString *theLabel = JNFJavaToNSString(env, label);
+    NSString *theKeyEquivalent = nil;
+    unichar macKey = shortcutKey;
+
+    if (macKey == 0) {
+        macKey = AWTKeyToMacShortcut(shortcutKeyCode, (mods & java_awt_event_KeyEvent_SHIFT_MASK) != 0);
+    }
+
+    if (macKey != 0) {
+        unichar equivalent[1] = {macKey};
+        theKeyEquivalent = [NSString stringWithCharacters:equivalent length:1];
+    } else {
+        theKeyEquivalent = @"";
+    }
+
+    [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaLabel:theLabel shortcut:theKeyEquivalent modifierMask:mods];
+JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CMenuItem
+ * Method:    nativeSetTooltip
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CMenuItem_nativeSetTooltip
+(JNIEnv *env, jobject peer, jlong menuItemObj, jstring tooltip)
+{
+JNF_COCOA_ENTER(env);
+    NSString *theTooltip = JNFJavaToNSString(env, tooltip);
+    [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaToolTipText:theTooltip];
+JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CMenuItem
+ * Method:    nativeSetImage
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CMenuItem_nativeSetImage
+(JNIEnv *env, jobject peer, jlong menuItemObj, jlong image)
+{
+JNF_COCOA_ENTER(env);
+    [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaImage:(NSImage*)jlong_to_ptr(image)];
+JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CMenuItem
+ * Method:    nativeCreate
+ * Signature: (JZ)J
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_lwawt_macosx_CMenuItem_nativeCreate
+    (JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator)
+{
+
+    CMenuItem *aCMenuItem = nil;
+    CMenu *parentCMenu = (CMenu *)jlong_to_ptr(parentCMenuObj);
+JNF_COCOA_ENTER(env);
+
+    jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer);
+
+    NSMutableArray *args = nil;
+
+    // Create a new item....
+    if (isSeparator == JNI_TRUE) {
+        args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:YES],  nil];
+    } else {
+        args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:NO],  nil];
+    }
+
+    [ThreadUtilities performOnMainThread:@selector(_createMenuItem_OnAppKitThread:) onObject:[CMenuItem alloc] withObject:args waitUntilDone:YES awtMode:YES];
+
+    aCMenuItem = (CMenuItem *)[args objectAtIndex: 0];
+
+    if (aCMenuItem == nil) {
+        return 0L;
+    }
+
+    // and add it to the parent item.
+    [parentCMenu addJavaMenuItem: aCMenuItem];
+
+    // setLabel will be called after creation completes.
+
+    if (aCMenuItem) {
+        CFRetain(aCMenuItem); // GC
+        [aCMenuItem release];
+    }
+
+JNF_COCOA_EXIT(env);
+    return ptr_to_jlong(aCMenuItem);
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CMenuItem
+ * Method:    nativeSetEnabled
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CMenuItem_nativeSetEnabled
+(JNIEnv *env, jobject peer, jlong menuItemObj, jboolean enable)
+{
+JNF_COCOA_ENTER(env);
+    CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
+    [item setJavaEnabled: (enable == JNI_TRUE)];
+JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CCheckboxMenuItem
+ * Method:    nativeSetState
+ * Signature: (IZ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetState
+(JNIEnv *env, jobject peer, jlong menuItemObj, jboolean state)
+{
+JNF_COCOA_ENTER(env);
+    CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
+    [item setJavaState: (state == JNI_TRUE)];
+JNF_COCOA_EXIT(env);
+}
+
+/*
+ * Class:     sun_lwawt_macosx_CCheckboxMenuItem
+ * Method:    nativeSetState
+ * Signature: (IZ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetIsCheckbox
+(JNIEnv *env, jobject peer, jlong menuItemObj)
+{
+JNF_COCOA_ENTER(env);
+    CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
+    [item setIsCheckbox];
+JNF_COCOA_EXIT(env);
+}