javax.swing.text.JTextComponentのオブジェクトの
getDocumentメソッドで得られるDocumentオブジェクトには
取り消し可能なオペレーションを処理するためのオブジェクトを
登録するaddUndoableEditListenerメソッドがあります。
これに使われるインターフェイスがUndoableEditListenerで、
それに使用されるがDocumentオブジェクトと連動する操作クラスがUndoManagerです。
つまり、addUndoableEditListenerで指定したUndoableEditListenerの
undoableEditHappenedメソッドが、エディタ内容変更のタイミングで呼び出されるように作られています。
そこで、UndoableEditListenerサブクラスのundoableEditHappenedを
オーバーライドするようにして、
更新情報をUndoManagerオブジェクトに記録させます。
あとは、各やり直しのオペレーションで、UndoManagerオブジェクトを操作する形態に作ります。
以下では、編集結果を元すAction用としてUndoActionクラスを作り、
やり直しAction用としてUndoActionRedoActionクラスを作って、keyMapに追加しています。
import java.awt.*;
import java.awt.event.*;//InputEvent
import javax.swing.*;//KeyStroke
import javax.swing.text.*;//Keymap
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.UndoManager;
public class Test extends JFrame {
JTextPane jtext = new JTextPane();
//------------Undo関連-------------------------------
UndoManager undoManager = new UndoManager();//UndoやRedoを管理する
UndoAction undoAction = new UndoAction();//Undo処理用Actionサブクラス
RedoAction redoAction = new RedoAction();
class UndoAction extends AbstractAction {
UndoAction() {
putValue(Action.NAME, "元に戻す");
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
undoManager.undo();
updateState();
redoAction.updateState();
}
protected void updateState() {
if (undoManager.canUndo()) {
setEnabled(true);
} else {
setEnabled(false);
}
}
}
class RedoAction extends AbstractAction {
RedoAction() {
putValue(Action.NAME, "やり直し");
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
undoManager.redo();
updateState();
undoAction.updateState();
}
protected void updateState() {
if (undoManager.canRedo()) {
setEnabled(true);
} else {
setEnabled(false);
}
}
}
class UndoRedoListener implements UndoableEditListener {
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
undoAction.updateState();
redoAction.updateState();
}
}
Test() {//コンストラクタ
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.jtext.setPreferredSize(new Dimension(160,120));
this.getContentPane().add(jtext, BorderLayout.CENTER);
Keymap keymap = jtext.getKeymap();
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Z,InputEvent.CTRL_DOWN_MASK),
undoAction);
keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_Y,InputEvent.CTRL_DOWN_MASK),
redoAction);
Document doc = this.jtext.getDocument();
doc.addUndoableEditListener(new UndoRedoListener());
this.pack();
this.setVisible(true);
}
public static void main(String[] args) {
new Test();
}
}
キーストローク以外に左記メニューでもUndoを行うことができる変更を以下に示します。
ですが、 と の部分は、上記と全く同じプログラムで作っています。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.UndoManager;
public class Test extends JFrame {
JTextPane jtext = new JTextPane();
//------------Undo関連-------------------------------
UndoManager undoManager = new UndoManager();//UndoやRedoを管理する
UndoAction undoAction = new UndoAction();//Undo処理用Actionサブクラス
RedoAction redoAction = new RedoAction();
class UndoAction extends AbstractAction {
UndoAction() {
putValue(Action.NAME, "元に戻す");
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
undoManager.undo();
updateState();
redoAction.updateState();
}
protected void updateState() {
if (undoManager.canUndo()) {
setEnabled(true);
} else {
setEnabled(false);
}
}
}
class RedoAction extends AbstractAction {
RedoAction() {
putValue(Action.NAME, "やり直し");
setEnabled(false);
}
public void actionPerformed(ActionEvent e) {
undoManager.redo();
updateState();
undoAction.updateState();
}
protected void updateState() {
if (undoManager.canRedo()) {
setEnabled(true);
} else {
setEnabled(false);
}
}
}
class UndoRedoListener implements UndoableEditListener {
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
undoAction.updateState();
redoAction.updateState();
}
}
Action getActionByName(String act_name){//エディタの既に使えるAction群より、引数の名前のActionを取得する。
Action[] array = jtext.getActions();
for (int i = 0; i < array.length; i++) {
String name = (String)array[i].getValue(Action.NAME);
if (name.equals(act_name)) return array[i];
}
return null;
}
Test() {//コンストラクタ
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.jtext.setPreferredSize(new Dimension(160,120));
this.getContentPane().add(jtext, BorderLayout.CENTER);
//メニュー作成
JMenuBar menuBar = new javax.swing.JMenuBar();
this.getRootPane().setJMenuBar(menuBar);//メニューバーのセット
JMenu menuEdit = new JMenu("編集(E)");
menuEdit.setMnemonic('E');
menuBar.add(menuEdit);
JMenuItem itemUndo = new JMenuItem(undoAction);
menuEdit.add(itemUndo);//メニュー項目追加
itemUndo.setAccelerator(KeyStroke.getKeyStroke("ctrl Z"));
JMenuItem itemRedo = new JMenuItem(redoAction);
menuEdit.add(itemRedo);//メニュー項目追加
itemRedo.setAccelerator(KeyStroke.getKeyStroke("ctrl Y"));
menuEdit.addSeparator();
Action actionCut=getActionByName("cut-to-clipboard");
actionCut.putValue(Action.NAME, "切り取り");
JMenuItem itemCut = new JMenuItem(actionCut);
itemCut.setAccelerator(KeyStroke.getKeyStroke("ctrl X"));
menuEdit.add(itemCut);//メニュー項目追加
Action actionCopy=getActionByName("copy-to-clipboard");
actionCopy.putValue(Action.NAME, "コピー");
JMenuItem itemCopy = new JMenuItem(actionCopy);
itemCopy.setAccelerator(KeyStroke.getKeyStroke("ctrl C"));
menuEdit.add(itemCopy);//メニュー項目追加
Action actionPaste=getActionByName("paste-from-clipboard");
actionPaste.putValue(Action.NAME, "貼り付け");
JMenuItem itemPaste = new JMenuItem(actionPaste);
itemPaste.setAccelerator(KeyStroke.getKeyStroke("ctrl V"));
menuEdit.add(itemPaste);//メニュー項目追加
Document doc = this.jtext.getDocument();
doc.addUndoableEditListener(new UndoRedoListener());
this.pack();
this.setVisible(true);
}
public static void main(String[] args) {
new Test();
}
}