I'm trying to disable the default behavior of the ALT key in Windows Look and Feel when a JTextArea is focused and the frame has a JMenu installed. What happens by default is that the menu gets the keyboard focus and therefore you can't continue typing there.

My first attempt was to capture single ALT presses by adding the key to the JTextArea InputMap:

//Disable ALT key presses when the textarea is focused
jta.getInputMap(javax.swing.JComponent.WHEN_FOCUSED).put(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ALT, 0, true), "ALTKEY");
jta.getActionMap().put("ALTKEY", new javax.swing.AbstractAction() {
    private static final long serialVersionUID = 1L;
    @Override
    public void actionPerformed(java.awt.event.ActionEvent e) {
        System.out.println("Performed");
    }
});

I noticed this worked as expected in some third party VMs but not in the latest from Oracle 1.8.0_211

Then I tried using key listeners from the AWT component and consume the event before it reaches other components.

//Disable ALT key presses when the textarea is focused, second attempt
jta.addKeyListener(new java.awt.event.KeyListener(){
    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
            System.out.println("Pressed");
            e.consume();
        }
    }
    @Override
    public void keyReleased(KeyEvent e) {
        if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
            System.out.println("Released");
            e.consume();
        }
    }
    @Override
    public void keyTyped(KeyEvent e) {
        if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
            System.out.println("Typed");
            e.consume();
        }
    }
});

No luck.

Finally, I tried to change the key used to create the menu shortcuts and get completely rid of that annoying ALT behavior:

//Disable the ALT for the Mnemonic and put CTRL:
int[] ints = {java.awt.event.KeyEvent.CTRL_MASK};
javax.swing.UIManager.put("Menu.shortcutKeys", ints);

This avoided the use of shortcuts with ALT key (and CTRL worked perfectly), but didn't prevent the standard behavior when a single ALT press is done.

Changing the L&F to other platforms, or not setting it to the Windows one solves the problem, but of course, it changes the look of the whole Program.

I guess I'm doing something wrong, but changing the InputMap or adding the KeyListeners worked for a third party VM, so I'm starting to think this might be a bug.

Any ideas on how to find who is listening to ALT key presses and avoid it? Is there a way to customize the L&F or see what is its configuration?

Here is the complete code of the test:

public class JMenubarAltTest extends javax.swing.JFrame{
    private static final long serialVersionUID = 1L;

    public JMenubarAltTest(){
        super();

        //Disable the ALT for the Mnemonic and put CTRL key instead:
        int[] ints = {java.awt.event.KeyEvent.CTRL_MASK};
        javax.swing.UIManager.put("Menu.shortcutKeys", ints);

        //Put the platform L&F, in my case I need this to work in Windows
        try {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException e1) {
            e1.printStackTrace();
        }

        this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

        //Create a random menu with some random items
        javax.swing.JMenuBar jmb = new javax.swing.JMenuBar();
        javax.swing.JMenu jm1 = new javax.swing.JMenu("menu1");
        jm1.setMnemonic(java.awt.event.KeyEvent.VK_M);
        jm1.add(new javax.swing.JMenuItem("item1"));
        jm1.add(new javax.swing.JMenuItem("item2"));
        jm1.add(new javax.swing.JMenuItem("item3"));

        javax.swing.JMenu jm2 = new javax.swing.JMenu("menu1");
        jm2.setMnemonic(java.awt.event.KeyEvent.VK_E);
        jm2.add(new javax.swing.JMenuItem("item1"));
        jm2.add(new javax.swing.JMenuItem("item2"));
        jm2.add(new javax.swing.JMenuItem("item3"));

        jmb.add(jm1);
        jmb.add(jm2);
        this.setJMenuBar(jmb);

        //Get a JTextArea to show what happens with the focus
        javax.swing.JTextArea jta = new javax.swing.JTextArea();

        //Disable ALT key presses when the textarea is focused
        jta.getInputMap(javax.swing.JComponent.WHEN_FOCUSED).put(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ALT, 0, true), "ALTKEY");
        jta.getActionMap().put("ALTKEY", new javax.swing.AbstractAction() {
            private static final long serialVersionUID = 1L;
            @Override
            public void actionPerformed(java.awt.event.ActionEvent e) {
                System.out.println("Performed");
            }
        });

        //Disable ALT key presses when the textarea is focused, second attempt
        jta.addKeyListener(new java.awt.event.KeyListener(){
            @Override
            public void keyPressed(java.awt.event.KeyEvent e) {
                if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
                    System.out.println("Pressed");
                    e.consume();
                }
            }
            @Override
            public void keyReleased(java.awt.event.KeyEvent e) {
                if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
                    System.out.println("Released");
                    e.consume();
                }
            }
            @Override
            public void keyTyped(java.awt.event.KeyEvent e) {
                if(e.getKeyCode() == java.awt.event.KeyEvent.VK_ALT){
                    System.out.println("Typed");
                    e.consume();
                }
            }
        });

        this.getContentPane().add(jta);
        this.pack();
        this.setSize(200, 200);
        this.setVisible(true);
    }

    public static void main(java.lang.String[] args){
        new JMenubarAltTest();
    }
}

1 Answers

1
Sergiy Medvynskyy On Best Solutions

Unfortunatelly the alt processor in Windows L&F for some reasons ignores the consumed flag. So we need to remove it using reflection API.

import java.awt.BorderLayout;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.lang.reflect.Method;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.JTextField;

public class JMenubarAltTest extends javax.swing.JFrame {
    private static final long serialVersionUID = 1L;

    private KeyEventPostProcessor altPostprocessor;

    public JMenubarAltTest() {
        super();

        // Disable the ALT for the Mnemonic and put CTRL key instead:
        int[] ints = {java.awt.event.KeyEvent.CTRL_MASK};
        javax.swing.UIManager.put("Menu.shortcutKeys", ints);

        // Put the platform L&F, in my case I need this to work in Windows
        try {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                | javax.swing.UnsupportedLookAndFeelException e1) {
            e1.printStackTrace();
        }

        this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

        // Create a random menu with some random items
        javax.swing.JMenuBar jmb = new javax.swing.JMenuBar();
        javax.swing.JMenu jm1 = new javax.swing.JMenu("menu1");
        jm1.setMnemonic(java.awt.event.KeyEvent.VK_M);
        jm1.add(new javax.swing.JMenuItem("item1"));
        jm1.add(new javax.swing.JMenuItem("item2"));
        jm1.add(new javax.swing.JMenuItem("item3"));

        javax.swing.JMenu jm2 = new javax.swing.JMenu("menu1");
        jm2.setMnemonic(java.awt.event.KeyEvent.VK_E);
        jm2.add(new javax.swing.JMenuItem("item1"));
        jm2.add(new javax.swing.JMenuItem("item2"));
        jm2.add(new javax.swing.JMenuItem("item3"));

        jmb.add(jm1);
        jmb.add(jm2);
        this.setJMenuBar(jmb);

        // Get a JTextArea to show what happens with the focus
        javax.swing.JTextArea jta = new javax.swing.JTextArea();

        // Disable ALT key presses when the textarea is focused
        jta.addFocusListener(new FocusListener() {

            @Override
            public void focusLost(FocusEvent e) {
                addAltPostprocessor();
            }

            @Override
            public void focusGained(FocusEvent e) {
                removeAltProcessor();
            }
        });

        this.getContentPane().add(new JScrollPane(jta));
        this.getContentPane().add(new JTextField(), BorderLayout.SOUTH);
        this.setSize(400, 300);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    // use reflection to remove the Windows L&F ALT processor.
    private void removeAltProcessor() {
        if (altPostprocessor == null) {
            try {
                Method method = KeyboardFocusManager.class.getDeclaredMethod("getKeyEventPostProcessors");
                method.setAccessible(true);
                @SuppressWarnings("unchecked")
                List<KeyEventPostProcessor> list =
                        (List<KeyEventPostProcessor>) method.invoke(KeyboardFocusManager.getCurrentKeyboardFocusManager());
                for (KeyEventPostProcessor pp : list) {
                    if (pp.getClass().getName().contains("WindowsRootPaneUI")) {
                        KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(pp);
                        altPostprocessor = pp;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(altPostprocessor);
        }
    }

    private void addAltPostprocessor() {
        if (altPostprocessor != null) {
            KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(altPostprocessor);
        }
    }

    public static void main(java.lang.String[] args) {
        new JMenubarAltTest();
    }
}

In this example ALT processing is disabled when text area is focused. For text field is alt processing still enabled.