Search code examples
javaswingjlistjmenuitemacceleratorkey

Is there a way to override native accelerator keys?


I have a JList filled with entries that the native copy method can't handle. I have a method that effectively copies and formats the selected data in just the way I want and I want this method to be called when ctrl + c is used but unfortunately JList is already listing for those accelerator keys so when I set them for my copy function in my JMenu it doesn't work unless I give the JMeny focus. Is there anyway that I can make ctrl + c use my copy method instead of the native one other than having to click on the JMenu every time I want to copy something?


Solution

  • Start by taking a look at How to Use Key Bindings and How to Use Actions

    Start by creating a Action which describes what it does and how it does. In this case, you may need to supply the JList from which you want to copy from (I'd have some kind of interface which provided a simply "copy" method of some kind, but that's just me)

    public class CopyAction extends AbstractAction {
    
        private JList list;
    
        public CopyAction(JList list) {
            putValue(NAME, "Copy");
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK));
            putValue(MNEMONIC_KEY, KeyEvent.VK_C);
            putValue(SELECTED_KEY, "Copy stuff");
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("All your copies belong to us");
        }
    
        public JList getList() {
            return list;
        }
    
    }
    

    Now, add a key binding to a JList, along with your Action...

    public class TestPane extends JPanel {
    
        private JList listOfStuff;
    
        public TestPane() {
            setLayout(new BorderLayout());
            listOfStuff = new JList();
            add(new JScrollPane(listOfStuff));
    
            CopyAction copyAction = new CopyAction(listOfStuff);
            listOfStuff.getActionMap().put("copy", copyAction);
        }
    
        public JList getListOfStuff() {
            return listOfStuff;
        }
    
    }
    

    Now, you can use the Action with buttons as well...

    TestPane tp = new TestPane();
    CopyAction copyAction = new CopyAction(tp.getListOfStuff());
    
    JMenuBar mb = new JMenuBar();
    JMenu menu = new JMenu("Edit");
    mb.add(menu);
    menu.add(copyAction);
    
    JFrame frame = new JFrame("Testing");
    frame.setJMenuBar(mb);
    

    And a fully runnable example....

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import javax.swing.AbstractAction;
    import javax.swing.JFrame;
    import javax.swing.JList;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.KeyStroke;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public class CopyAction extends AbstractAction {
    
            private JList list;
    
            public CopyAction(JList list) {
                putValue(NAME, "Copy");
                putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK));
                putValue(MNEMONIC_KEY, KeyEvent.VK_C);
                putValue(SELECTED_KEY, "Copy stuff");
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("All your copies belong to us");
            }
    
            public JList getList() {
                return list;
            }
    
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    TestPane tp = new TestPane();
                    CopyAction copyAction = new CopyAction(tp.getListOfStuff());
    
                    JMenuBar mb = new JMenuBar();
                    JMenu menu = new JMenu("Edit");
                    mb.add(menu);
                    menu.add(copyAction);
    
                    JFrame frame = new JFrame("Testing");
                    frame.setJMenuBar(mb);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private JList listOfStuff;
    
            public TestPane() {
                setLayout(new BorderLayout());
                listOfStuff = new JList();
                add(new JScrollPane(listOfStuff));
    
                CopyAction copyAction = new CopyAction(listOfStuff);
                listOfStuff.getActionMap().put("copy", copyAction);
            }
    
            public JList getListOfStuff() {
                return listOfStuff;
            }
    
        }
    
    }