8194873: right ALT key hotkeys no longer work in Swing components
Reviewed-by: serb, psadhukhan
--- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java Mon Jun 25 12:50:25 2018 +0530
@@ -636,7 +636,9 @@
"Menu.submenuPopupOffsetX", -2,
"Menu.submenuPopupOffsetY", 3,
"Menu.shortcutKeys", new int[]{
- SwingUtilities2.getSystemMnemonicKeyMask(), metaMask
+ SwingUtilities2.getSystemMnemonicKeyMask(), metaMask,
+ SwingUtilities2.setAltGraphMask(
+ SwingUtilities2.getSystemMnemonicKeyMask())
},
"Menu.cancelMode", "hideMenuTree",
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java Mon Jun 25 12:50:25 2018 +0530
@@ -26,6 +26,7 @@
package javax.swing.plaf.basic;
import sun.swing.DefaultLookup;
+import sun.swing.SwingUtilities2;
import sun.swing.UIAction;
import java.awt.*;
import java.awt.event.*;
@@ -156,8 +157,17 @@
map.clear();
map.put(KeyStroke.getKeyStroke(m, BasicLookAndFeel.getFocusAcceleratorKeyMask(), false),
"pressed");
+ map.put(KeyStroke.getKeyStroke(m, SwingUtilities2.setAltGraphMask
+ (BasicLookAndFeel.getFocusAcceleratorKeyMask()),
+ false),
+ "pressed");
+
map.put(KeyStroke.getKeyStroke(m, BasicLookAndFeel.getFocusAcceleratorKeyMask(), true),
"released");
+ map.put(KeyStroke.getKeyStroke(m,
+ SwingUtilities2.setAltGraphMask
+ (BasicLookAndFeel.getFocusAcceleratorKeyMask()), true),
+ "released");
map.put(KeyStroke.getKeyStroke(m, 0, true), "released");
}
else {
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java Mon Jun 25 12:50:25 2018 +0530
@@ -36,6 +36,7 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.Component;
import java.awt.Container;
@@ -402,6 +403,10 @@
}
inputMap.clear();
inputMap.put(KeyStroke.getKeyStroke(dka, BasicLookAndFeel.getFocusAcceleratorKeyMask(), false), "press");
+ inputMap.put(KeyStroke.getKeyStroke(dka,
+ SwingUtilities2.setAltGraphMask (
+ BasicLookAndFeel.getFocusAcceleratorKeyMask()),
+ false), "press");
}
else {
InputMap inputMap = SwingUtilities.getUIInputMap
@@ -520,6 +525,8 @@
int dka = label.getDisplayedMnemonic();
putOnRelease(inputMap, dka, BasicLookAndFeel
.getFocusAcceleratorKeyMask());
+ putOnRelease(inputMap, dka, SwingUtilities2.setAltGraphMask (
+ BasicLookAndFeel.getFocusAcceleratorKeyMask()));
// Need this when the sticky keys are enabled
putOnRelease(inputMap, dka, 0);
// Need this if ALT is released before the accelerator
@@ -539,6 +546,9 @@
int dka = label.getDisplayedMnemonic();
removeOnRelease(inputMap, dka, BasicLookAndFeel
.getFocusAcceleratorKeyMask());
+ removeOnRelease(inputMap, dka,
+ SwingUtilities2.setAltGraphMask (
+ BasicLookAndFeel.getFocusAcceleratorKeyMask()));
removeOnRelease(inputMap, dka, 0);
removeOnRelease(inputMap, KeyEvent.VK_ALT, 0);
}
@@ -555,6 +565,9 @@
} else {
putOnRelease(inputMap, dka, BasicLookAndFeel
.getFocusAcceleratorKeyMask());
+ putOnRelease(inputMap, dka,
+ SwingUtilities2.setAltGraphMask (
+ BasicLookAndFeel.getFocusAcceleratorKeyMask()));
// Need this when the sticky keys are enabled
putOnRelease(inputMap, dka, 0);
}
@@ -572,6 +585,9 @@
if (isCommand) {
removeOnRelease(inputMap, dka, BasicLookAndFeel
.getFocusAcceleratorKeyMask());
+ removeOnRelease(inputMap, dka,
+ SwingUtilities2.setAltGraphMask (
+ BasicLookAndFeel.getFocusAcceleratorKeyMask()));
removeOnRelease(inputMap, dka, 0);
} else {
removeOnRelease(inputMap, KeyEvent.VK_ALT, 0);
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java Mon Jun 25 12:50:25 2018 +0530
@@ -1093,7 +1093,9 @@
"Menu.submenuPopupOffsetX", 0,
"Menu.submenuPopupOffsetY", 0,
"Menu.shortcutKeys", new int[]{
- SwingUtilities2.getSystemMnemonicKeyMask()
+ SwingUtilities2.getSystemMnemonicKeyMask(),
+ SwingUtilities2.setAltGraphMask(
+ SwingUtilities2.getSystemMnemonicKeyMask())
},
"Menu.crossMenuMnemonic", Boolean.TRUE,
// Menu.cancelMode affects the cancel menu action behaviour;
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java Mon Jun 25 12:50:25 2018 +0530
@@ -434,6 +434,7 @@
return null;
}
+ @SuppressWarnings("deprecation")
void updateAcceleratorBinding() {
KeyStroke accelerator = menuItem.getAccelerator();
InputMap windowInputMap = SwingUtilities.getUIInputMap(
@@ -450,6 +451,45 @@
JComponent.WHEN_IN_FOCUSED_WINDOW, windowInputMap);
}
windowInputMap.put(accelerator, "doClick");
+
+ int modifiers = accelerator.getModifiers();
+ if (((modifiers & InputEvent.ALT_DOWN_MASK) != 0) &&
+ ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) {
+ //When both ALT and ALT_GRAPH are set, add the ALT only
+ // modifier keystroke which is used for left ALT key.
+ // Unsetting the ALT_GRAPH will do that as ALT is already set
+ modifiers &= ~InputEvent.ALT_GRAPH_DOWN_MASK;
+ modifiers &= ~InputEvent.ALT_GRAPH_MASK;
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
+ modifiers, accelerator.isOnKeyRelease());
+ windowInputMap.put(keyStroke, "doClick");
+ } else if (((modifiers & InputEvent.ALT_DOWN_MASK) != 0) && (
+ (modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) == 0)) {
+ //When only ALT modifier is set, add the ALT + ALT_GRAPH
+ // modifier keystroke which is used for right ALT key
+ modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
+ modifiers, accelerator.isOnKeyRelease());
+ windowInputMap.put(keyStroke, "doClick");
+ } else if ((modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) {
+ //When only ALT_GRAPH is set, remove the ALT_GRAPH only
+ // modifier and add the ALT and ALT+ALT_GRAPH modifiers
+ // keystroke which are used for left ALT key and right ALT
+ // respectively
+ modifiers &= ~InputEvent.ALT_GRAPH_DOWN_MASK;
+ modifiers &= ~InputEvent.ALT_GRAPH_MASK;
+
+ modifiers |= InputEvent.ALT_DOWN_MASK;
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
+ modifiers, accelerator.isOnKeyRelease());
+ windowInputMap.put(keyStroke, "doClick");
+
+ //Add ALT+ALT_GRAPH modifier which is used for right ALT key
+ modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
+ keyStroke = KeyStroke.getKeyStroke(accelerator.getKeyCode(),
+ modifiers, accelerator.isOnKeyRelease());
+ windowInputMap.put(keyStroke, "doClick");
+ }
}
}
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java Mon Jun 25 12:50:25 2018 +0530
@@ -129,7 +129,8 @@
int[] shortcutKeys = (int[])DefaultLookup.get(menuItem, this,
"Menu.shortcutKeys");
if (shortcutKeys == null) {
- shortcutKeys = new int[] {KeyEvent.ALT_MASK};
+ shortcutKeys = new int[] {KeyEvent.ALT_MASK,
+ KeyEvent.ALT_MASK | KeyEvent.ALT_GRAPH_MASK};
}
if (mnemonic == lastMnemonic) {
return;
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java Mon Jun 25 12:50:25 2018 +0530
@@ -606,6 +606,10 @@
}
mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic, BasicLookAndFeel.getFocusAcceleratorKeyMask()),
"setSelectedIndex");
+ mnemonicInputMap.put(KeyStroke.getKeyStroke(mnemonic,
+ SwingUtilities2.setAltGraphMask(
+ BasicLookAndFeel.getFocusAcceleratorKeyMask())),
+ "setSelectedIndex");
mnemonicToIndexMap.put(Integer.valueOf(mnemonic), Integer.valueOf(index));
}
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java Mon Jun 25 12:50:25 2018 +0530
@@ -42,6 +42,8 @@
import javax.swing.plaf.synth.SynthUI;
import sun.swing.DefaultLookup;
import sun.awt.AppContext;
+import sun.swing.SwingUtilities2;
+
import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
/**
@@ -511,6 +513,10 @@
km.clear();
if (accelerator != '\0') {
km.put(KeyStroke.getKeyStroke(accelerator, BasicLookAndFeel.getFocusAcceleratorKeyMask()), "requestFocus");
+ km.put(KeyStroke.getKeyStroke(accelerator,
+ SwingUtilities2.setAltGraphMask(
+ BasicLookAndFeel.getFocusAcceleratorKeyMask())),
+ "requestFocus");
}
}
}
--- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java Mon Jun 25 12:50:25 2018 +0530
@@ -2139,6 +2139,15 @@
return -1;
}
+ /**
+ * Sets the InputEvent.ALT_GRAPH mask on any modifier passed to the function
+ * @param modifier the modifier passed
+ * @return the modifier retiurned with ALT_GRAPH flag set
+ */
+ public static int setAltGraphMask(int modifier) {
+ return (modifier | InputEvent.ALT_GRAPH_DOWN_MASK);
+ }
+
@SuppressWarnings("deprecation")
public static int getSystemMnemonicKeyMask() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
--- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp Mon Jun 25 16:01:01 2018 +0530
+++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp Mon Jun 25 12:50:25 2018 +0530
@@ -3537,15 +3537,10 @@
BOOL shiftIsDown = FALSE;
if (modifiers) {
shiftIsDown = modifiers & java_awt_event_InputEvent_SHIFT_DOWN_MASK;
- BOOL altIsDown = modifiers & java_awt_event_InputEvent_ALT_DOWN_MASK;
+ BOOL altIsDown = ((modifiers & java_awt_event_InputEvent_ALT_DOWN_MASK) ||
+ (modifiers & java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK));
BOOL ctrlIsDown = modifiers & java_awt_event_InputEvent_CTRL_DOWN_MASK;
- // Windows treats AltGr as Ctrl+Alt
- if (modifiers & java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK) {
- altIsDown = TRUE;
- ctrlIsDown = TRUE;
- }
-
if (shiftIsDown) {
keyboardState[VK_SHIFT] |= KEY_STATE_DOWN;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/swing/event/RightAltKeyTest.java Mon Jun 25 12:50:25 2018 +0530
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @key headful
+ * @bug 8194873
+ * @requires (os.family == "Windows")
+ * @summary Checks that right ALT (ALT_GRAPH) key works on Swing components
+ * @run main RightAltKeyTest
+ */
+
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.KeyStroke;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.GridLayout;
+import java.awt.Robot;
+import java.lang.reflect.InvocationTargetException;
+
+public class RightAltKeyTest {
+
+ boolean action = false;
+ JFrame frame;
+
+ void testJMenu() {
+ frame = new JFrame("Menu Frame");
+ JMenuBar mb = new JMenuBar();
+ JMenu m1 = new JMenu("File");
+ JMenuItem i1 = new JMenuItem("Save");
+ JMenuItem i2 = new JMenuItem("Load");
+
+ m1.setMnemonic(KeyEvent.VK_F);
+
+ m1.addMenuListener(new MenuListener() {
+ @Override
+ public void menuSelected(MenuEvent e) {
+ action = true;
+ disposeUI();
+ }
+
+ @Override
+ public void menuDeselected(MenuEvent e) {
+ }
+
+ @Override
+ public void menuCanceled(MenuEvent e) {
+ }
+ });
+
+ frame.setJMenuBar(mb);
+ mb.add(m1);
+ m1.add(i1);
+ m1.add(i2);
+
+ frame.setSize(200, 200);
+ frame.setVisible(true);
+ }
+
+ void testJMenuItem() {
+ frame = new JFrame("Menu Frame");
+ JMenuBar mb = new JMenuBar();
+ JMenu m1 = new JMenu("File");
+ JMenuItem i1 = new JMenuItem("Save");
+ i1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
+ InputEvent.ALT_GRAPH_DOWN_MASK));
+ i1.addActionListener((e) -> {
+ action = true;
+ disposeUI();
+ });
+
+ frame.setJMenuBar(mb);
+ mb.add(m1);
+ m1.add(i1);
+
+ frame.setSize(200, 200);
+ frame.setVisible(true);
+ }
+
+ void testJOptionPane() {
+ int selection = JOptionPane.showConfirmDialog(null, "Do you wish " +
+ "to save file?","Confirm", JOptionPane.YES_NO_CANCEL_OPTION);
+ //Pressed Yes
+ if (selection == 0) {
+ action = true;
+ }
+ }
+
+ void testJTabbedPane() {
+ frame =new JFrame();
+ JPanel p1=new JPanel();
+ JPanel p2=new JPanel();
+ JTabbedPane tp=new JTabbedPane();
+ tp.add("Main",p1);
+ tp.add("Visit",p2);
+ tp.setMnemonicAt(0, KeyEvent.VK_M);
+ tp.setMnemonicAt(1, KeyEvent.VK_V);
+
+ tp.addChangeListener((e) -> {
+ if (tp.getSelectedIndex() == 1)
+ action = true;
+ disposeUI();
+ });
+
+ frame.add(tp);
+ frame.setSize(200,200);
+ frame.setVisible(true);
+ }
+
+ void testJTextArea() {
+ JTextField firstField = new JTextField(10);
+ JTextField lastField = new JTextField(10);
+
+ JLabel firstLabel = new JLabel("First Name", JLabel.RIGHT);
+ firstLabel.setDisplayedMnemonic('F');
+ firstLabel.setLabelFor(firstField);
+
+ JLabel lastLabel = new JLabel("Last Name", JLabel.RIGHT);
+ lastLabel.setDisplayedMnemonic('L');
+ lastLabel.setLabelFor(lastField);
+
+ JPanel p = new JPanel();
+ p.setLayout(new GridLayout(2, 2, 5, 5));
+ p.add(firstLabel);
+ p.add(firstField);
+ p.add(lastLabel);
+ p.add(lastField);
+
+ frame = new JFrame("MnemonicLabels");
+ lastField.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ action = true;
+ disposeUI();
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+
+ }
+ });
+
+ frame.add(p);
+ frame.setSize(200,200);
+ frame.setVisible(true);
+ }
+
+ void test() throws Exception {
+ UIManager.LookAndFeelInfo[] lookAndFeels = UIManager
+ .getInstalledLookAndFeels();
+ for (UIManager.LookAndFeelInfo lookAndFeel : lookAndFeels) {
+ UIManager.setLookAndFeel(lookAndFeel.getClassName());
+
+ Robot robot = new Robot();
+ robot.setAutoDelay(100);
+ robot.waitForIdle();
+
+ action = false;
+ SwingUtilities.invokeLater(this::testJMenu);
+ robot.waitForIdle();
+ robot.keyPress(KeyEvent.VK_ALT_GRAPH);
+ robot.keyPress(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_F);
+ robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
+ robot.waitForIdle();
+ if (!action)
+ errLog("JMenu", lookAndFeel.getClassName());
+
+ action = false;
+ SwingUtilities.invokeLater(this::testJMenuItem);
+ robot.waitForIdle();
+ robot.keyPress(KeyEvent.VK_ALT_GRAPH);
+ robot.keyPress(KeyEvent.VK_S);
+ robot.keyRelease(KeyEvent.VK_S);
+ robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
+ robot.waitForIdle();
+ if (!action)
+ errLog("JMenuItem", lookAndFeel.getClassName());
+
+ action = false;
+ SwingUtilities.invokeLater(this::testJOptionPane);
+ robot.waitForIdle();
+ robot.keyPress(KeyEvent.VK_ALT_GRAPH);
+ robot.keyPress(KeyEvent.VK_Y);
+ robot.keyRelease(KeyEvent.VK_Y);
+ robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
+ robot.waitForIdle();
+ if (!action)
+ errLog("JOptionPane", lookAndFeel.getClassName());
+
+ action = false;
+ SwingUtilities.invokeLater(this::testJTabbedPane);
+ robot.waitForIdle();
+ robot.keyPress(KeyEvent.VK_ALT_GRAPH);
+ robot.keyPress(KeyEvent.VK_V);
+ robot.keyRelease(KeyEvent.VK_V);
+ robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
+ robot.waitForIdle();
+ if (!action)
+ errLog("JTabbedPane", lookAndFeel.getClassName());
+
+ action = false;
+ SwingUtilities.invokeLater(this::testJTextArea);
+ robot.waitForIdle();
+ robot.keyPress(KeyEvent.VK_ALT_GRAPH);
+ robot.keyPress(KeyEvent.VK_L);
+ robot.keyRelease(KeyEvent.VK_L);
+ robot.keyRelease(KeyEvent.VK_ALT_GRAPH);
+ robot.waitForIdle();
+ if (!action)
+ errLog("JTextArea", lookAndFeel.getClassName());
+ }
+ System.out.println("Passed.");
+ }
+
+ void disposeUI() {
+ frame.setVisible(false);
+ frame.dispose();
+ }
+
+ void errLog(String componentName, String lookAndFeel)
+ throws InvocationTargetException, InterruptedException
+ {
+ SwingUtilities.invokeAndWait(this::disposeUI);
+ throw new RuntimeException("Actions are not performed for "+
+ componentName + " with " + lookAndFeel + " look and feel.");
+ }
+
+ public static void main(String[] args) throws Exception {
+ RightAltKeyTest t = new RightAltKeyTest();
+ t.test();
+ }
+}