概要

JTableのセルエディタに、CopyPasteUndoRedoなどを行うJPopupMenuを設定します。

スクリーンショット

CellEditorPopupMenu.png

サンプルコード

class TextComponentPopupMenu extends JPopupMenu {
  protected TextComponentPopupMenu(JTextComponent tc) {
    super();
    Action cutAction = new DefaultEditorKit.CutAction();
    add(cutAction);
    Action copyAction = new DefaultEditorKit.CopyAction();
    add(copyAction);
    Action pasteAction = new DefaultEditorKit.PasteAction();
    add(pasteAction);
    Action deleteAction = new DeleteAction();
    add(deleteAction);
    addSeparator();

    UndoManager manager = new UndoManager();
    Action undoAction = new UndoAction(manager);
    add(undoAction);

    Action redoAction = new RedoAction(manager);
    add(redoAction);

    tc.addAncestorListener(new AncestorListener() {
      @Override public void ancestorAdded(AncestorEvent e) {
        manager.discardAllEdits();
        e.getComponent().requestFocusInWindow();
      }

      @Override public void ancestorMoved(AncestorEvent e) {
        /* not needed */
      }

      @Override public void ancestorRemoved(AncestorEvent e) {
        /* not needed */
      }
    });
    tc.getDocument().addUndoableEditListener(manager);
    tc.getActionMap().put("undo", undoAction);
    tc.getActionMap().put("redo", redoAction);
    InputMap im = tc.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    // Java 10: int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, mask), "undo");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, mask), "redo");

    addPopupMenuListener(new PopupMenuListener() {
      @Override public void popupMenuCanceled(PopupMenuEvent e) {
        /* not needed */
      }

      @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        undoAction.setEnabled(true);
        redoAction.setEnabled(true);
      }

      @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        JTextComponent tc = (JTextComponent) getInvoker();
        boolean hasSelectedText = Objects.nonNull(tc.getSelectedText());
        cutAction.setEnabled(hasSelectedText);
        copyAction.setEnabled(hasSelectedText);
        deleteAction.setEnabled(hasSelectedText);
        undoAction.setEnabled(manager.canUndo());
        redoAction.setEnabled(manager.canRedo());
      }
    });
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、JTableのセルエディタ内の文字列に対して、CutCopyPasteDeleteUndoRedoを行うためのJPopupMenuを設定しています。


  • セルエディタとして使用するJTextFieldは、同一インスタンス使い回しているため、別セルでの編集が持ち越されないようAncestorListenerを使って表示されるたびにUndoManager#discardAllEdits()を呼び出してリセット
    • もしくはDefaultCellEditor#isCellEditable(...)などをオーバーライドしてリセット
DefaultCellEditor ce = new DefaultCellEditor(new JTextField()) {
  @Override public boolean isCellEditable(EventObject e) {
    boolean b = super.isCellEditable(e);
    if (b) {
      manager.discardAllEdits();
    }
    return b;
  }
};
table.setDefaultEditor(Object.class, ce);

参考リンク

コメント