Java undo and redo in a Swing application

Here's a step-by-step approach for how to implement "undo" and "redo" behavior in your Java text components, i.e., the JTextArea JEditorPane and JTextPane I should note that you don't get this behavior for free -- you have to implement it yourself, hence this undo/redo tutorial.

Here are the steps you need to follow to implement Java undo/redo behavior, with source code taken from a Java/Swing project I'm currently working on.

Declare your Java undo and redo fields

First, declare the objects/references you're going to need to at the top of your Java class. Here are the specific undo/redo fields required in my current Java class:

// undo and redo
private Document editorPaneDocument;
protected UndoHandler undoHandler = new UndoHandler();
protected UndoManager undoManager = new UndoManager();
private UndoAction undoAction = null;
private RedoAction redoAction = null;

Let's see how these objects are used in my Java application.

Add the UndoableEditListener to your Java Document

Next, add your instance of a UndoableEditListener to the Document object you get from your JTextComponent In my case I'm using a JTextPane, and I get a reference to the JTextPane's Document object like this:

// in my case i'm using a JTextPane, but you can use a JEditorPane
// or JTextArea
editorPaneDocument = jTextPane.getDocument();
editorPaneDocument.addUndoableEditListener(undoHandler);

Regardless of which JTextComponent you use, you will also get your reference to the Document object the same way.

Assign your Undo and Redo actions to your text component inputmap

Next, because my undo and redo behavior is implemented as Java Action classes, I can easily add these actions to the input map of my JTextPane, as shown here:

KeyStroke undoKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.META_MASK);
KeyStroke redoKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, Event.META_MASK);

undoAction = new UndoAction();
jTextPane.getInputMap().put(undoKeystroke, "undoKeystroke");
jTextPane.getActionMap().put("undoKeystroke", undoAction);

redoAction = new RedoAction();
jTextPane.getInputMap().put(redoKeystroke, "redoKeystroke");
jTextPane.getActionMap().put("redoKeystroke", redoAction);

Put the undo and redo actions on the Edit menu

You can also put the undo and redo actions on the Edit menu of your Java/Swing application if you prefer. In my case this wasn't too important, I was developing an application for myself, but it's certainly a good thing to do for GUI applications the public will use:

// Edit menu
JMenu editMenu = new JMenu("Edit");
JMenuItem undoMenuItem = new JMenuItem(undoAction);
JMenuItem redoMenuItem = new JMenuItem(redoAction);
editMenu.add(undoMenuItem);
editMenu.add(redoMenuItem);

The Java Undo and Redo classes

Given all that background material, we now get to the classes that implement the undo and redo behavior, i.e., the classes you'll need to make all the lines of code above work properly. Quite honestly, I don't know where I got this code from, but I'll assume it was from the JFC Swing Tutorial or the Core JFC or Core Swing books that are sitting on my bookshelves, but I don't know for sure.

What I do know is that I pulled these classes from a Java Swing application I wrote about eight years ago, and they work just as well today for undo and redo behavior as they did way back then.

Here is the source code for these Java undo and redo classes:

// java undo and redo action classes

class UndoHandler implements UndoableEditListener
{

  /**
   * Messaged when the Document has created an edit, the edit is added to
   * <code>undoManager</code>, an instance of UndoManager.
   */
  public void undoableEditHappened(UndoableEditEvent e)
  {
    undoManager.addEdit(e.getEdit());
    undoAction.update();
    redoAction.update();
  }
}

class UndoAction extends AbstractAction
{
  public UndoAction()
  {
    super("Undo");
    setEnabled(false);
  }

  public void actionPerformed(ActionEvent e)
  {
    try
    {
      undoManager.undo();
    }
    catch (CannotUndoException ex)
    {
      // TODO deal with this
      //ex.printStackTrace();
    }
    update();
    redoAction.update();
  }

  protected void update()
  {
    if (undoManager.canUndo())
    {
      setEnabled(true);
      putValue(Action.NAME, undoManager.getUndoPresentationName());
    }
    else
    {
      setEnabled(false);
      putValue(Action.NAME, "Undo");
    }
  }
}

class RedoAction extends AbstractAction
{
  public RedoAction()
  {
    super("Redo");
    setEnabled(false);
  }

  public void actionPerformed(ActionEvent e)
  {
    try
    {
      undoManager.redo();
    }
    catch (CannotRedoException ex)
    {
      // TODO deal with this
      ex.printStackTrace();
    }
    update();
    undoAction.update();
  }

  protected void update()
  {
    if (undoManager.canRedo())
    {
      setEnabled(true);
      putValue(Action.NAME, undoManager.getRedoPresentationName());
    }
    else
    {
      setEnabled(false);
      putValue(Action.NAME, "Redo");
    }
  }
}

Summary: Java undo and redo

I hope this short-but-sweet introduction of Java undo and redo code for Swing text components will be helpful to you. I think I've included everything you'll need to add undo and redo behavior to your Java text components -- JTextArea, JEditorPane, JTextPane -- but if not, let me know, and I'll add whatever I might have missed.