---
category: swing
folder: UndoableEditTable
title: JTableのセル編集をUndo可能にする
title-en: Making JTable cell edits undoable
tags: [JTable, UndoManager, UndoableEdit]
author: aterai
pubdate: 2025-12-15T00:58:29+09:00
description: JTableのCellEditorで実行したセル値の編集をキー入力でUndo、Redo可能に設定します。
summary-jp: JTableのCellEditorで実行したセル値の編集をキー入力でUndo、Redo可能に設定します。
summary-en: Enables undo and redo of cell value edits performed in a JTable's CellEditor via keyboard input.
image: https://drive.google.com/uc?id=1W7lnyGT5xYgJw3RT7SEFfPbguGLiB-Ny
---
* Summary [#summary]
`JTable`の`CellEditor`で実行したセル値の編集をキー入力で`Undo`、`Redo`可能に設定します。
// #en{{Enables undo and redo of cell value edits performed in a `JTable`'s `CellEditor` via keyboard input.}}

#download(https://drive.google.com/uc?id=1W7lnyGT5xYgJw3RT7SEFfPbguGLiB-Ny)

* Source Code Examples [#sourcecode]
#code(link){{
class PropertyTable extends JTable {
  private static final int TARGET_COLUMN = 1;
  private Class<?> editingClass;
  private final UndoManager undoManager = new UndoManager();

  protected PropertyTable(TableModel model) {
    super(model);
    initUndoActions();
  }

  private void initUndoActions() {
    String undo = "undo";
    String redo = "redo";
    ActionMap am = getActionMap();
    am.put(undo, new UndoAction());
    am.put(redo, new RedoAction());
    InputMap im = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    im.put(KeyStroke.getKeyStroke("ctrl Z"), undo);
    im.put(KeyStroke.getKeyStroke("ctrl shift Z"), redo);
    im.put(KeyStroke.getKeyStroke("ctrl Y"), redo);
  }


  @Override public void setValueAt(Object newValue, int row, int column) {
    if (isEditing()) {
      int mr = convertRowIndexToModel(row);
      int mc = convertColumnIndexToModel(column);
      Object oldValue = getValueAt(mr, mc);
      if (!Objects.equals(oldValue, newValue)) {
        TableModel m = getModel();
        undoManager.addEdit(new UndoableCellEdit(m, mr, mc, oldValue, newValue));
      }
    }
    super.setValueAt(newValue, row, column);
  }

  private class UndoAction extends AbstractAction {
    private UndoAction() {
      super("undo");
    }

    @Override public void actionPerformed(ActionEvent e) {
      try {
        undoManager.undo();
      } catch (CannotUndoException ex) {
        UIManager.getLookAndFeel().provideErrorFeedback(
            (Component) e.getSource());
      }
    }
  }

  private class RedoAction extends AbstractAction {
    private RedoAction() {
      super("redo");
    }

    @Override public void actionPerformed(ActionEvent e) {
      try {
        undoManager.redo();
      } catch (CannotRedoException ex) {
        UIManager.getLookAndFeel().provideErrorFeedback(
            (Component) e.getSource());
      }
    }
  }
  // ...
}
}}

* Description [#description]
- `JTable#setValueAt(...)`をオーバーライドしてセル値が現在のセル値と異なる場合は以下のような`AbstractUndoableEdit`を実装する`UndoableCellEdit`を作成し、`UndoManager#addEdit(...)`で追加
    - `UndoManager#undo()`を実行するアクションを`JTable`の`ActionMap`に追加し、KBD{Ctrl+Z}キー入力でその`Undo`アクションを実行するよう`InputMap`に追加
    - `UndoManager#redo()`を実行するアクションも同様に`JTable`の`ActionMap`に追加し、KBD{Ctrl+Y}、またはKBD{Ctrl+Shift+Z}キー入力でその`Redo`アクションを実行するよう`InputMap`に追加
-- `UndoManager#undo()`を実行するアクションを`JTable`の`ActionMap`に追加し、KBD{Ctrl+Z}キー入力でその`Undo`アクションを実行するよう`InputMap`に追加
-- `UndoManager#redo()`を実行するアクションも同様に`JTable`の`ActionMap`に追加し、KBD{Ctrl+Y}、またはKBD{Ctrl+Shift+Z}キー入力でその`Redo`アクションを実行するよう`InputMap`に追加

#code{{
class UndoableCellEdit extends AbstractUndoableEdit {
  private final TableModel model;
  private final int row;
  private final int column;
  private final Object oldValue;
  private final Object newValue;

  protected UndoableCellEdit(
        TableModel m, int row, int col, Object oldVal, Object newVal) {
    super();
    this.model = m;
    this.row = row;
    this.column = col;
    this.oldValue = oldVal;
    this.newValue = newVal;
  }

  @Override public void undo() {
    super.undo();
    model.setValueAt(oldValue, row, column);
  }

  @Override public void redo() {
    super.redo();
    model.setValueAt(newValue, row, column);
  }
}
}}

* Reference [#reference]
- [[JTableのセルエディタにJPopupMenuを設定>Swing/CellEditorPopupMenu]]

* Comment [#comment]
#comment
#comment