Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set an accelerator for a JMenu sub menu?

I have a user request to add an accelerator to a sub menu (JMenu) which would allow the user to press the short cut and have the corresponding sub menu "fold out", showing its contained menu items.

I don't recall every having seen something like this (either in Java or any other language). Our application is written in Java using Swing. We have a number of JMenuItems with accelerators that work well, but when I attempted to add an accelerator to JMenu I get the following exception:

java.lang.Error: setAccelerator() is not defined for JMenu. Use setMnemonic() instead.

I've tried to use the MenuDemo! code to experiment with this a bit further.

This is what I tried:

//a submenu
menu.addSeparator();
submenu = new JMenu("A submenu");
submenu.setMnemonic(KeyEvent.VK_S);
submenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, InputEvent.CTRL_MASK));

The last line is the one added by me, which causes the exception.

I've tried extensive googling but all I can find is articles on how to add accelerators to JMenuItem.

It seems the JMenu does not support this natively. Is there any workaround to achieve this behaviour?

like image 403
Urs Beeli Avatar asked Oct 16 '12 07:10

Urs Beeli


People also ask

How do I create a swing menu?

In order to create menu items in Swing, you need to create new instances of JMenuItem and set different properties for them. You can create menu item with both text and icon. Creates a JMenuItem instance without icon or text. Creates a JMenuItem instance with a given icon.

What is the use of JMenuItem in Java?

Creates a menu item whose properties are taken from the specified Action .


2 Answers

Another option is to override the accelerator get/set and reproduce the JMenuItem behaviour. Then the UI will do the rest of the job.

The important thing is to fire the property change and have a consistent get/set for the accelerator. The advantage of this solution is that it also provides a visual indication of the shortcut/accelerator.

Here is a small demo code:

import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestMenu {

    protected void initUI() {
        JFrame frame = new JFrame(TestMenu.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JMenuBar bar = new JMenuBar();
        JMenu topMenu = new JMenu("Top Menu");
        JMenu subMenu = new JMenu("Sub menu") {
            private KeyStroke accelerator;

            @Override
            public KeyStroke getAccelerator() {
                return accelerator;
            }

            @Override
            public void setAccelerator(KeyStroke keyStroke) {
                KeyStroke oldAccelerator = accelerator;
                this.accelerator = keyStroke;
                repaint();
                revalidate();
                firePropertyChange("accelerator", oldAccelerator, accelerator);
            }
        };
        subMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.CTRL_MASK));
        JMenuItem item1 = new JMenuItem("Item 1");
        JMenuItem item2 = new JMenuItem("Item 2");
        subMenu.add(item1);
        subMenu.addSeparator();
        subMenu.add(item2);
        topMenu.add(subMenu);
        bar.add(topMenu);
        frame.setJMenuBar(bar);
        frame.setSize(400, 300);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (UnsupportedLookAndFeelException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                new TestMenu().initUI();
            }
        });
    }

}
like image 101
Guillaume Polet Avatar answered Sep 28 '22 08:09

Guillaume Polet


I don't think that is possible just like that. But what you could do is adding an AbstractAction, which simulates a click.

submenu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control U"), "expand");
    submenu.getActionMap().put("expand", new AbstractAction("expand") {
        private static final long serialVersionUID = 1L;

        @Override
        public void actionPerformed(ActionEvent evt) {
            submenu.doClick();
        }
    }
);

I hope this also works for you.

like image 40
1r0n1k Avatar answered Sep 28 '22 08:09

1r0n1k