Search code examples
javaswingjtextareajtextpaneundo-redo

UndoableEditListener never called in JTextPane/JTextArea


I am trying to attach an UndoableEditListener to a JTextPane or JTextArea that queues up edits into an UndoManager.

textPane.getDocument().addUndoableEditListener(new UndoableEditListener() {
    @Override
    public void undoableEditHappened(UndoableEditEvent event) {
        undoQueue.addEdit(event.getEdit());
    }
});

But undoableEditHappened is never called when I type "aaa" in the text window.

Thinking it's Java's fault, not mine, I crack AbstractDocument.class open with Eclipse debugger to watch the event trigger. It has a private listeners array. AbstractDocument stores all its listeners in odd indices in the listeners array, with the listeners' type Class<>'s in the even indices.

protected void fireUndoableEditUpdate(UndoableEditEvent e) {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for (int i = listeners.length - 2; i >= 0; i -= 2) {
        if (listeners[i] == UndoableEditListener.class) {
            // Lazily create the event:
            // if (e == null)
            // e = new ListSelectionEvent(this, firstIndex, lastIndex);
            ((UndoableEditListener) listeners[i + 1]).undoableEditHappened(e);
        }
    }
}

See the line if (listeners[i] == UndoableEditListener.class)? When I add the undo change listener, the debugger shows listeners containing my listener, along with UndoableEditListener.class in the index before it. But, when the debugger comes to that if-statement, all the even indices in the array listeners show as DocumentListener.class in the debugger. Consequently, the if-statement is always false and the listener never called.

What the heck? Is this a Java 8 bug? Or am I missing a step the examples forgot to mention?


Solution

  • The problem was in the JTextPane. I was overriding its setText method to force it to call read, the alternative to setText that normalizes all kinds of newline while remembering them. But JTextPane.read appears to not trigger an UndoableEditEvent on the document.

    If I leave setText alone, then UndoManager.undo works.