# HG changeset patch # User pbansal # Date 1529911225 -19800 # Node ID 86897f8a6598b983a017176be0bb97533239d58a # Parent b9456394d24fa48966882a835fcc85c9da551ca0 8194873: right ALT key hotkeys no longer work in Swing components Reviewed-by: serb, psadhukhan diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java --- 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", diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonListener.java --- 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 { diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java --- 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); diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java --- 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; diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java --- 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"); + } } } diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicMenuUI.java --- 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; diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTabbedPaneUI.java --- 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)); } diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java --- 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"); } } } diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/share/classes/sun/swing/SwingUtilities2.java --- 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(); diff -r b9456394d24f -r 86897f8a6598 src/java.desktop/windows/native/libawt/windows/awt_Component.cpp --- 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; } diff -r b9456394d24f -r 86897f8a6598 test/jdk/javax/swing/event/RightAltKeyTest.java --- /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(); + } +}