TITLE:UndoManagerを使用した文字列選択ペーストの動作を変更する

Posted by at 2012-10-15

UndoManagerを使用した文字列選択ペーストの動作を変更する

`JTextFieldなどにUndoManagerを設定し、文字列を選択してペーストした後のUndo`の動作を変更します。

  • &jnlp;
  • &jar;
  • &zip;
ReplaceUndoableEdit.png

サンプルコード

Document doc = new PlainDocument() {
  @Override public void replace(
      int offset, int length, String text, AttributeSet attrs)
        throws BadLocationException {
    if(length!=0) { //replace
      undoManager.undoableEditHappened(
        new UndoableEditEvent(this, new ReplaceUndoableEdit(offset, length, text)));
      replaceIgnoringUndo(offset, length, text, attrs);
    }else{ //insert
      super.replace(offset, length, text, attrs);
    }
  }
  private void replaceIgnoringUndo(
      int offset, int length, String text, AttributeSet attrs)
        throws BadLocationException {
    removeUndoableEditListener(undoManager);
    super.replace(offset, length, text, attrs);
    addUndoableEditListener(undoManager);
  }
  class ReplaceUndoableEdit extends AbstractUndoableEdit {
    private final String oldValue;
    private final String newValue;
    private int offset;
    public ReplaceUndoableEdit(int offset, int length, String newValue) {
      String txt;
      try{
        txt = getText(offset, length);
      }catch(BadLocationException e) {
        txt = null;
      }
      this.oldValue = txt;
      this.newValue = newValue;
      this.offset = offset;
    }
    @Override public void undo() throws CannotUndoException {
      try{
        replaceIgnoringUndo(offset, newValue.length(), oldValue, null);
      }catch(BadLocationException ex) {
        throw new CannotUndoException();
      }
    }
    @Override public void redo() throws CannotRedoException {
      try{
        replaceIgnoringUndo(offset, oldValue.length(), newValue, null);
      }catch(BadLocationException ex) {
        throw new CannotUndoException();
      }
    }
    @Override public boolean canUndo() {
      return true;
    }
    @Override public boolean canRedo() {
      return true;
    }
  }
};
View in GitHub: Java, Kotlin

解説

  • 上: デフォルト
    • `JTextComponent#setText(String)や、文字列を選択してペーストした場合、Document#replace(...)で実行されるDocument#remove(...)Document#insertString(...)が別々にUndoManagerに登録される仕様?なので、二回Undo`しないとペースト前の状態に戻らない
    • `Document#replace(...)をオーバーライドし、直接UndoManager#undoableEditHappened(...)`を使って取り消し可能な編集を登録
    • 実際の置換は、`removeUndoableEditListener(...)UndoManagerを一時的に削除した後に行う(直後にaddUndoableEditListener()`で再登録)
    • 登録する`UndoableEditでのUndo, Redo時の置換もUndoManager`を一時的に削除して行う
    • メモ: このサンプルでは選択状態を復元していない

参考リンク

コメント