8152492: [macosx swing] double key event actions when using Mac menubar
authoraniyogi
Wed, 27 Apr 2016 12:08:37 +0400
changeset 38384 05e90f2c9ab8
parent 38383 c76fffbf4ef5
child 38385 218b99b653c4
8152492: [macosx swing] double key event actions when using Mac menubar Reviewed-by: serb, mhalder, alexsch
jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m
jdk/test/javax/swing/JMenuItem/ActionListenerCalledTwice/ActionListenerCalledTwiceTest.java
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m	Mon Apr 25 19:14:30 2016 +0300
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m	Wed Apr 27 12:08:37 2016 +0400
@@ -25,7 +25,6 @@
 
 #import <JavaNativeFoundation/JavaNativeFoundation.h>
 #include <Carbon/Carbon.h>
-
 #import "CMenuItem.h"
 #import "CMenu.h"
 #import "AWTEvent.h"
@@ -64,42 +63,6 @@
 - (BOOL) worksWhenModal {
     return YES;
 }
-// This is a method written using Carbon framework methods to remove
-// All modifiers including "Shift" modifier.
-// Example 1: Shortcut set is "Command Shift m" returns "m"
-// Example 2: Shortcut set is "Command m" returns "m"
-// Example 3: Shortcut set is "Alt Shift ," returns ","
-
-CFStringRef createStringForKey(CGKeyCode keyCode)
-{
-    TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
-//  currentKeyboard now contains the current input source
-    CFDataRef layoutData =
-    TISGetInputSourceProperty(currentKeyboard,
-                              kTISPropertyUnicodeKeyLayoutData);
-//  the UNICODE keyLayout is fetched from currentKeyboard in layoutData
-    const UCKeyboardLayout *keyboardLayout =
-    (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
-//  A read-only data pointer is fetched from layoutData
-    UInt32 keysDown = 0;
-    UniChar chars[4];
-    UniCharCount realLength;
-    
-    UCKeyTranslate(keyboardLayout,
-                   keyCode,
-                   kUCKeyActionDisplay,
-                   0,
-                   LMGetKbdType(),
-                   kUCKeyTranslateNoDeadKeysBit,
-                   &keysDown,
-                   sizeof(chars) / sizeof(chars[0]),
-                   &realLength,
-                   chars);
-    CFRelease(currentKeyboard);
-//  Converts keyCode, modifier and dead-key state into UNICODE characters
-    return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
-}
-
 // Events
 - (void)handleAction:(NSMenuItem *)sender {
     AWT_ASSERT_APPKIT_THREAD;
@@ -116,35 +79,6 @@
     // from this "frameless" menu, because there are no active windows. This
     // means we have to handle it here.
     NSEvent *currEvent = [[NSApplication sharedApplication] currentEvent];
-    if ([currEvent type] == NSKeyDown) {
-        NSString *menuKey = [sender keyEquivalent];
-//      If shortcut is "Command Shift ," the menuKey gets the value ","
-//      But [currEvent charactersIgnoringModifiers]; returns "<" and not ","
-//      because the charactersIgnoreingModifiers does not ignore "Shift"
-//      So a shortcut like "Command Shift m" will return "M" where as the
-//      MenuKey will have the value "m". To remove this issue the below
-//      createStringForKey is used.
-        NSString *eventKey = createStringForKey([currEvent keyCode]);
-        
-//      Apple uses characters from private Unicode range for some of the
-//      keys, so we need to do the same translation here that we do
-//      for the regular key down events
-                if ([eventKey length] == 1) {
-                    unichar origChar = [eventKey characterAtIndex:0];
-                    unichar newChar =  NsCharToJavaChar(origChar, 0);
-                    if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) {
-                        newChar = origChar;
-                    }
-        
-                    eventKey = [NSString stringWithCharacters: &newChar length: 1];
-                }
-        
-        NSWindow *keyWindow = [NSApp keyWindow];
-        if ([menuKey isEqualToString:eventKey] && keyWindow != nil) {
-            return;
-        }
-    }
-    
     if (fIsCheckbox) {
         static JNF_CLASS_CACHE(jc_CCheckboxMenuItem, "sun/lwawt/macosx/CCheckboxMenuItem");
         static JNF_MEMBER_CACHE(jm_ckHandleAction, jc_CCheckboxMenuItem, "handleAction", "(Z)V");
@@ -154,16 +88,47 @@
         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)
-        
-        NSUInteger modifiers = [currEvent modifierFlags];
-        jint javaModifiers = NsKeyModifiersToJavaModifiers(modifiers, NO);
-        
-        JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)
+    }
+    else {
+        if ([currEvent type] == NSKeyDown) {
+            
+            // Event available through sender variable hence NSApplication
+            // not needed for checking the keyboard input sans the modifier keys
+            // Also, the method used to fetch eventKey earlier would be locale dependent
+            // With earlier implementation, if MenuKey: e EventKey: ा ; if input method
+            // is not U.S. (Devanagari in this case)
+            // With current implementation, EventKey = MenuKey = e irrespective of
+            // input method
+            
+            NSString *eventKey = [sender keyEquivalent];
+            // Apple uses characters from private Unicode range for some of the
+            // keys, so we need to do the same translation here that we do
+            // for the regular key down events
+            if ([eventKey length] == 1) {
+                unichar origChar = [eventKey characterAtIndex:0];
+                unichar newChar =  NsCharToJavaChar(origChar, 0);
+                if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) {
+                    newChar = origChar;
+                }
+                eventKey = [NSString stringWithCharacters: &newChar length: 1];
+            }
+            NSWindow *keyWindow = [NSApp keyWindow];
+            if (keyWindow != nil) {
+                return;
+            }
+            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)
+                
+                NSUInteger modifiers = [currEvent modifierFlags];
+                jint javaModifiers = NsKeyModifiersToJavaModifiers(modifiers, NO);
+                
+                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 {
--- a/jdk/test/javax/swing/JMenuItem/ActionListenerCalledTwice/ActionListenerCalledTwiceTest.java	Mon Apr 25 19:14:30 2016 +0300
+++ b/jdk/test/javax/swing/JMenuItem/ActionListenerCalledTwice/ActionListenerCalledTwiceTest.java	Wed Apr 27 12:08:37 2016 +0400
@@ -21,23 +21,23 @@
  * questions.
  */
 
-/*
+ /*
  * @test
- * @bug 7160951
+ * @bug 7160951 8152492
  * @summary [macosx] ActionListener called twice for JMenuItem using ScreenMenuBar
  * @author vera.akulova@oracle.com
  * @library ../../../../lib/testlibrary
  * @build jdk.testlibrary.OSInfo
  * @run main ActionListenerCalledTwiceTest
  */
-
 import jdk.testlibrary.OSInfo;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 
 public class ActionListenerCalledTwiceTest {
-    static String menuItems[] = { "Item1", "Item2", "Item3", "Item4", "Item5", "Item6" };
+
+    static String menuItems[] = {"Item1", "Item2", "Item3", "Item4", "Item5", "Item6"};
     static KeyStroke keyStrokes[] = {
         KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.META_MASK),
         KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
@@ -46,8 +46,10 @@
         KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_MASK),
         KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.META_MASK)
     };
+    static JMenu menu;
+    static JFrame frame;
+    static volatile int listenerCallCounter = 0;
 
-    static volatile int listenerCallCounter = 0;
     public static void main(String[] args) throws Exception {
         if (OSInfo.getOSType() != OSInfo.OSType.MACOSX) {
             System.out.println("This test is for MacOS only. Automatically passed on other platforms.");
@@ -82,33 +84,38 @@
             robot.waitForIdle();
 
             if (listenerCallCounter != 1) {
-                throw new Exception("Test failed: ActionListener for " + menuItems[i] +
-                    " called " + listenerCallCounter + " times instead of 1!");
+                throw new Exception("Test failed: ActionListener for " + menuItems[i]
+                        + " called " + listenerCallCounter + " times instead of 1!");
             }
 
             listenerCallCounter = 0;
         }
+        SwingUtilities.invokeAndWait(new Runnable() {
+            public void run() {
+                frame.dispose();
+            }
+        });
     }
 
     private static void createAndShowGUI() {
-        JMenu menu = new JMenu("Menu");
+        menu = new JMenu("Menu");
 
         for (int i = 0; i < menuItems.length; ++i) {
             JMenuItem newItem = new JMenuItem(menuItems[i]);
             newItem.setAccelerator(keyStrokes[i]);
             newItem.addActionListener(
-                new ActionListener(){
-                    public void actionPerformed(ActionEvent e) {
-                        listenerCallCounter++;
-                    }
+                    new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    listenerCallCounter++;
                 }
+            }
             );
             menu.add(newItem);
         }
 
         JMenuBar bar = new JMenuBar();
         bar.add(menu);
-        JFrame frame = new JFrame("Test");
+        frame = new JFrame("Test");
         frame.setJMenuBar(bar);
         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         frame.pack();