8152492: [macosx swing] double key event actions when using Mac menubar

Reviewed-by: serb, mhalder, alexsch
This commit is contained in:
Avik Niyogi 2016-04-27 12:08:37 +04:00
parent af802cb541
commit 5874c48796
2 changed files with 59 additions and 87 deletions
jdk
src/java.desktop/macosx/native/libawt_lwawt/awt
test/javax/swing/JMenuItem/ActionListenerCalledTwice

@ -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 @@ CFStringRef createStringForKey(CGKeyCode keyCode)
// 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 @@ CFStringRef createStringForKey(CGKeyCode keyCode)
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 {

@ -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 @@ public class ActionListenerCalledTwiceTest {
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;
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 @@ public class ActionListenerCalledTwiceTest {
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();