Java JMenuItem快捷键Ctrl-C/Ctrl-V或Ctrl-Insert/Shift-Insert don';我不再工作了

Java JMenuItem快捷键Ctrl-C/Ctrl-V或Ctrl-Insert/Shift-Insert don';我不再工作了,java,swing,jtable,copy-paste,jmenu,Java,Swing,Jtable,Copy Paste,Jmenu,我有一个更老的基于java swing的独立应用程序,它使用JFrame和JMenuBar,其中包含多个jMenus元素(以及相应的JMenuItem项) 在Windows(7和vista)上升级到最新的1.6.0_41(或1.7.x)JVM后,我注意到如果将JTable添加到框架中,带有快捷键Ctrl-C(或Ctrl-Insert)的菜单项将不再接收其ActionEvent。但是,如果通过鼠标单击访问菜单,则会调用菜单ActionListener。如果删除JTable,则快捷方式有效。如果我将

我有一个更老的基于java swing的独立应用程序,它使用JFrame和JMenuBar,其中包含多个jMenus元素(以及相应的JMenuItem项)

在Windows(7和vista)上升级到最新的1.6.0_41(或1.7.x)JVM后,我注意到如果将JTable添加到框架中,带有快捷键Ctrl-C(或Ctrl-Insert)的菜单项将不再接收其ActionEvent。但是,如果通过鼠标单击访问菜单,则会调用菜单ActionListener。如果删除JTable,则快捷方式有效。如果我将快捷方式组合更改为Ctrl-C或Ctrl-Insert(即Ctrl-L)以外的内容,则会调用ActionListener

它过去的工作方式(我刚刚在Windows Vista上用jvm 1.4确认了这一点——我知道这已经有一段时间了,因为该环境受到了任何严重的关注:)是,如果焦点位于可编辑字段内,Ctrl-C将在JTable内执行标准的“复制到剪贴板”功能。否则,我的菜单ActionListener是通过setAccelerator()方法指定的快捷方式调用的

看起来JTable实现在1.6.*中有所改变,以便在Windows上以不同方式处理Ctrl-C绑定的事件

在Mac OS(JVM 1.6.043)上运行此应用程序时,我可以看到ActionListener是通过Ctrl-C快捷方式调用的。尽管这可能是因为JTable使用Command-C而不是Ctrl-C来复制到Mac OS下的剪贴板

我已经提取了演示问题的代码的相关部分。如有任何建议,我们将不胜感激

public class TestFrame extends JFrame {

public TestFrame(String title) {

    super(title);
}

private void init() {

    getContentPane().setLayout(new BorderLayout());

    addMenu();
    addTable();

    // Change default exit operation
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    pack();
    setVisible(true);
}

private void addTable() {

    JTable jTable = new JTable(createTableModel());

    // Place table in JScrollPane
    JScrollPane scrollPane = new JScrollPane(jTable);

    // Add Table
    add(scrollPane, BorderLayout.CENTER);
}

private TableModel createTableModel() {

    Object[][] data = new Object[][]{ 
            {new Date(), "First Row, 2nd column", "First Row, 3rd column"},
            {new Date(), "Second Row, 2nd column", "Second Row, 3rd column"},
        };

    Object[] columnNames = new Object[]{"Date", "Type", "Description"};

    DefaultTableModel model = new DefaultTableModel(data, columnNames) {

        public boolean isCellEditable(int row, int column) {
            return column != 0;
        }

    };

    return model;
}

private void addMenu() {

    // Create the menu bar.
    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);

    JMenu editMenu = new JMenu("Edit");
    menuBar.add(editMenu);

    TestActionListener listener = new TestActionListener();
    JMenuItem menuItem = null;

    menuItem = new JMenuItem("Copy 1");
    menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, ActionEvent.CTRL_MASK));
    menuItem.addActionListener(listener);
    editMenu.add(menuItem);

    menuItem = new JMenuItem("Copy 2");
    menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
    menuItem.addActionListener(listener);
    editMenu.add(menuItem);

    menuItem = new JMenuItem("Copy 3");
    menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.CTRL_MASK));
    menuItem.addActionListener(listener);
    editMenu.add(menuItem);
}


public static void main(String[] args) {

    TestFrame frame = new TestFrame("Test");
    frame.init();
}


private static class TestActionListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {
        System.out.println("TestFrame.TestActionListener.actionPerformed(): e="+ e);
    }
}

}

如果您的问题是如何从表中删除Control+C绑定,则可以执行以下操作:

KeyStroke copy = KeyStroke.getKeyStroke("control C");
InputMap im = table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.getParent().remove(copy);

但是,这将从所有表中删除绑定

问题在于您的框架没有聚焦,并且在整个组件层次结构中没有具有聚焦的元素,这意味着没有人会“抓住”事件并尝试对其进行处理。由于
JMenuItem
将其快捷方式绑定到输入映射
JComponent。当在聚焦窗口中时,您的快捷方式不会“回答”事件

要解决此问题,请将焦点放在其中一个组件上,或直接放在
JFrame
(例如使用
frame.requestFocusInWindow();
)。这里有一个小例子:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class TestFrame extends JFrame {

    public TestFrame(String title) {

        super(title);
    }

    private void init() {

        getContentPane().setLayout(new BorderLayout());

        addMenu();
        addTable();

        // Change default exit operation
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        pack();
        setVisible(true);
    }

    private void addTable() {

        JTable jTable = new JTable();

        // Place table in JScrollPane
        JScrollPane scrollPane = new JScrollPane(jTable);

        // Add Table
        add(scrollPane, BorderLayout.CENTER);
    }

    private void addMenu() {

        // Create the menu bar.
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        JMenu editMenu = new JMenu("Edit");
        menuBar.add(editMenu);

        TestActionListener listener = new TestActionListener();
        JMenuItem menuItem = null;

        menuItem = new JMenuItem("Copy 1");
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.CTRL_MASK));
        menuItem.addActionListener(listener);
        editMenu.add(menuItem);

        menuItem = new JMenuItem("Copy 2");
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK));
        menuItem.addActionListener(listener);
        editMenu.add(menuItem);

        menuItem = new JMenuItem("Copy 3");
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, KeyEvent.CTRL_MASK));
        menuItem.addActionListener(listener);
        editMenu.add(menuItem);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TestFrame frame = new TestFrame("Test");
                frame.init();
                frame.requestFocusInWindow();
            }
        });
    }

    private static class TestActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("TestFrame.TestActionListener.actionPerformed(): e=" + e);
        }
    }
}
补充说明:

  • 如果不需要,不要扩展JFrame
  • 使用
    SwingUtilities.invokeLater()

使用
getMenuShortcutKeyMask()
,如图所示。我知道为什么只有Ctrl-C/Ctrl-Insert会导致问题。在1.4.x版之后的某个时候,JTable得到了增强,可以将所选表行的内容复制到剪贴板中。该操作绑定到Ctrl-C/Ctrl-Insert,它截取了用于JMenu定义的操作的键绑定。它解释了为什么其他键组合可以工作,但不能使用Ctrl-C。如果表不是空的,则聚焦帧无助于解决问题。我已经更新了原始代码,添加了一个测试数据模型,以证明问题仍然存在。这完全有道理。我认为问题在于添加了JTable的增强功能,可以将所选表行的内容复制到剪贴板中。该操作绑定到Ctrl-C/Ctrl-Insert,它截取了用于JMenu定义的操作的键绑定。它解释了为什么其他组合键可以工作,但不能使用Ctrl-C。感谢您提供了此解决方案,该解决方案禁用了行内容复制,但仍然允许将可编辑单元格内容复制到剪贴板,这正是我以前的工作方式。是否有办法同时执行这两个操作?JTable的动作监听器是否也能接收Ctrl-C事件和我的JMenu?我的自定义Jmenu操作将复制基础模型行,但不会干扰复制到剪贴板的文本行内容。我可以看出,在剪贴板中保存行内容有时是多么有用。事件只发送到单个组件。我不知道如何将它分派到多个组件。