8152492: [macosx swing] double key event actions when using Mac menubar
Reviewed-by: serb, mhalder, alexsch
This commit is contained in:
parent
af802cb541
commit
5874c48796
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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user