Search code examples
javaundo-redoredo

Why would UndoManager.canRedo() return false even though redo() works fine?


I'm getting conflicting results using certain methods from Java's undo package. In my program, I'm calling canRedo() on an UndoManager instance, which returns false. This would lead me to believe I'm unable to redo any action stored in the UndoManager at that very moment. Yet, when I try, the last-undone action is correctly redone and no CannotRedoException is thrown. To me, this behaviour seems contradictory, and I'm not sure what's causing it.

The code below is an isolated, single-threaded scratch file created just for this question.

import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;

class UndoManagerRedoScratch {
    public static void main(String[] args) {
        UndoManager actionList = new UndoManager();
        actionList.addEdit(new SomeUndoableEdit());

        /* See whether SomeUndoableEdit is undoable. */
        try {
            System.out.println("Action can be undone: " + actionList.canUndo());
            actionList.undo();
        } catch (Exception e) {
            System.out.println("Undo failed");
        }

        /* See whether SomeUndoableEdit is redoable. */
        try {
            System.out.println("Action can be redone: " + actionList.canRedo());
            actionList.redo();
        } catch (Exception e) {
            System.out.println("Redo failed");
        }
    }
}

class SomeUndoableEdit extends AbstractUndoableEdit {

    public SomeUndoableEdit() {
        System.out.println("SomeUndoableEdit has been created");
    }

    @Override
    public void undo() throws CannotUndoException {
        System.out.println("SomeUndoableEdit has been undone.");
    }

    @Override
    public void redo() throws CannotRedoException {
        System.out.println("SomeUndoableEdit has been redone.");
    }
}

Output:

SomeUndoableEdit has been created
Action can be undone: true
SomeUndoableEdit has been undone.
Action can be redone: false
SomeUndoableEdit has been redone.

As you can see, redo() executed successfully without throwing a CannotRedoException, yet canUndo() returned false. Again, this seems contradictory to me.

Any ideas?


Solution

  • According to some implementations in the jre like javax.swing.undo.StateEdit you are supposed to call super.undo() or super.redo() as first call in your overridden method.

    So on your case:

    class SomeUndoableEdit extends AbstractUndoableEdit {
    
        public SomeUndoableEdit() {
            System.out.println("SomeUndoableEdit has been created");
        }
    
        @Override
        public void undo() throws CannotUndoException {
            super.undo();
            System.out.println("SomeUndoableEdit has been undone.");
        }
    
        @Override
        public void redo() throws CannotRedoException {
            super.redo();
            System.out.println("SomeUndoableEdit has been redone.");
        }
    }