【问题标题】:How to map standard Copy action to a popup menu in Swing如何将标准复制操作映射到 Swing 中的弹出菜单
【发布时间】:2017-08-23 01:13:14
【问题描述】:

我对我的 Swing UI 有一个简单的愿望:有一个标准的 Copy 操作映射到组件的 InputMap。接下来在同一个组件中有一个弹出菜单,我想添加一个菜单项来运行复制操作,当然会显示 inputMap 中的键盘快捷键。

这是映射的 mac 版本,在this 的帮助下,我终于设法将其添加为通用规则,因为我意识到某些组件使用“复制”,而其他组件使用 DefaultEditorKit.copyAction:

inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), DefaultEditorKit.copyAction);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), "copy");

现在,我可以找到表格的操作,例如

ActionMap actionMap = myTable.getActionMap();
Action action = actionMap.get("copy");

现在,我使用 Action 创建 MenuItem:

JPopupMenu popupMenu = new JPopupMenu();
JMenuItem item = new JMenuItem(action);
popupMenu.add(item);
table.setComponentPopupMenu(popupMenu);

结果,我看到了菜单项,但它没有复制任何内容,尽管映射到同一操作的快捷键确实复制了。我什至可以定义快捷方式(我似乎必须自己定义,但也只是提示用户这些东西以某种方式联系在一起):

int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, MASK);
item.setAccelerator(keyStroke);

那么,我错过了什么?我什至试图专门定义动作监听器,但无济于事:

item.addActionListener(myTable.getActionForKeyStroke(keyStroke));

键盘快捷键自动工作听起来很有趣(我只需要弄清楚如何使 Cmd 键在 Mac 中工作而不是 Ctrl (只花了几个小时)),现在我无法链接菜单项绝不会影响现有的操作(即使再工作几个小时后)。

【问题讨论】:

  • 请参阅What is the JTable CTRL+C event's name? 了解一些替代方案。
  • 嗯,似乎 myTable.getInputMap().get(KeyStroke.getKeyStroke(KeyEvent.VK_C, MASK)) 给出了“null”。但是“copy”是 actionMap 中的名称,并且 actionMap.get("copy") 给出了一个有效的 Action。
  • 你忘了WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
  • 啊哈 - 不,我没有忘记 - 我不知道 :) 我承认,这是我第一次做相关的事情,所以我很挣扎,但我也没有期待任何如此复杂的事情......好吧,现在我从 InputMap 得到“复制”,正如预期的那样。 - 问题仍然存在:如何从代码或菜单条目触发该操作,以便它也可以与鼠标操作一起使用?
  • 你可以试试上面引用的@camickr 的方法。

标签: java swing


【解决方案1】:

回顾comments,这里有一个完整的实现——从引用here的文章中的original修改——使用addCopyAndSelectAll方法添加了一个标准的“复制”和“全选”菜单项。请注意,这主要适用于 JTable 控件,因为它们使用这些操作名称。正如我在对the other issue 的回答中提到的,其他组件类型可能会初始化为其他操作名称。

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;

/*
 * The ActionMapAction class is a convenience class that allows you to use an installed Action as an
 * Action or ActionListener on a separate component.
 *
 * It can be used on components like JButton or JMenuItem that support an Action as a property of
 * the component. Or it can be added to the same above components as an ActionListener.
 *
 * The benefit of this class is that a new ActionEvent will be created such that the source of the
 * event is the component the Action belongs to, not the component that was "clicked". Otherwise in
 * many cases a ClassCastException will be thrown when the Action is invoked.
 */
@SuppressWarnings("serial")
public class ActionMapAction extends AbstractAction {
  /**
   * @param parent
   * @param popupMenu
   */
  public static void addCopyAndSelectAll(JComponent parent, JPopupMenu popupMenu) {
    // Must use ActionMapAction to link between the MenuItem and Table
    // The "copy" action, for example must be called from a Table, not from MenuItem
    Action copyAction = new ActionMapAction("Copy", parent, "copy");

    JMenuItem copyItem = new JMenuItem(copyAction);
    int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, MASK));
    popupMenu.add(copyItem);

    Action selAction = new ActionMapAction(UiMessages.INSTANCE.get("Select All", parent, "selectAll");

    JMenuItem selItem = new JMenuItem(selAction);
    selItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, MASK));
    popupMenu.add(selItem);

    parent.setComponentPopupMenu(popupMenu);
  }

  private Action originalAction;
  private JComponent component;

  private String actionCommand = "";

  /**
   * Replace the default Action for the given KeyStroke with a custom Action
   *
   * @param name the name parameter of the Action
   * @param componet the component the Action belongs to
   * @param actionKey the key to identify the Action in the ActionMap
   */
  public ActionMapAction(String name, JComponent component, String actionKey) {
    super(name);

    originalAction = component.getActionMap().get(actionKey);

    if (originalAction == null) {
      String message = "no Action for action key: " + actionKey;
      throw new IllegalArgumentException(message);
    }

    this.component = component;
  }

  /**
   * Invoke the original Action using the original component as the source of the event.
   */
  @Override
  public void actionPerformed(ActionEvent e) {
    e = new ActionEvent(component, ActionEvent.ACTION_PERFORMED, actionCommand, e.getWhen(), e.getModifiers());

    originalAction.actionPerformed(e);
  }

  public void setActionCommand(String actionCommand) {
    this.actionCommand = actionCommand;
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-18
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 2012-07-10
    相关资源
    最近更新 更多