Compound Undo Manager
Posted by Rob Camick on October 27, 2008
Swing text components support undo/redo of text entered in a Document. Every time a change is made to the Document an UndoableEditEvent is generated. The UndoManager keeps track of these events and allows you to invoke undo/redo on them. The default implemetation of the UndoManager is to undo/redo individual events. This means each character you type is undone/redone separately. I don’t know about you, but I would rather undo/redo releated events together at the same time.
The first thing we need to do is to define what related events are. Characters typed sequentially into a Document or removed sequentially from a Document should be related. That is, if you type a few characters, use the backspace key to correct a typo and then continue typing, the events should be related. Also, any attribute changes created as a result of the characters entered should be related. This would happen, for example, when doing syntax highlighting as text is typed. However, if you manually position the caret at a different location in the Document and then start typing, you would start a new series of related events.
A class called CompoundEdit already exists and allows us to combine individual edits events into one. All we need to do is customize the UndoManger and implements the rules for combining these individual edits. The CompoundUndoManager class tries to implement these rules the best it can. Undo/redo support is then added to a text component with the following line of code:
CompoundUndoManager um = new CompoundUndoManager(textComponent);
You can then create menu items or buttons to invoke the undo/redo functionality of the UndoManager using the Actions provided:
JButton undo = new JButton(um.getUndoAction());
JButton redo = new JButton(um.getRedoAction());
Add the text component and the buttons to your GUI and you are ready to go.

As you can see from the above image, the Webstart demo will show how undo/redo is handled when using syntax highlighting and other attribute changes like bold, italics and underlining.
Try The Demo
– Using Java™ Web Start (JRE 6 required)
Get The Code
Related Reading
Implementing Undo and Redo
How To Use Actions
Java API: javax.swing.undo.UndoManager
Jos said
Hi Camickr,
whenever I read your work I get associations of me banging my head against a thick concrete wall while you simply walk around it and enter through a wide open back door. I like your site!
kind regards,
Jos
daveg said
Hey, this is incredibly helpful for my project! I was about to undertake this myself, and then discovered that you have shared an example already.
Even though there’s no copyright on your code, I’m asking out of courtesy if I can reuse your code verbatim.
Thanks!
- dave
Rob Camick said
No problem. Glad you found it usefull.
bob said
Note: Bobs multiple comments have been edited into a single comment, Rob
1) Im finding that cant redo exception is being thrown quite alot. Not sure why.
2) OK commenting out the following got rid of my CantRedoExceptions…
// Always start a new compound edit after an undo
compoundEdit = null;
commented this out and my redo works fine now.
3) Also caret position in the document is getting corrupted on redo.
4) Yeah had to remove all your refs to setCaretPosition, they were causing this
javax.swing.text.BadLocationException: Position not represented by view
Corruption and then the JTextPane became unusable.
5) Generally just not working.
Rob Camick said
Sorry you are having problems. I have updated the entry to include a Webstart app showing how I tested the class. I do not encounter the problems you describe. I did however make a small change in how attributes are undone. If you still encounter problems with the demo then a description on how to reproduce the problem would be helpfull for me to narrow down the problem. Maybe use the Contact Us page where you can provide more detail with the explanation.
Anonymous said
Many thanks for the code.
I’d like to incorporate most of it, with changes to http://code.google.com/p/jsyntaxpane</a<
Any restrictions? Would you like to be attributed in any particular way?
Rob Camick said
It yours to use at your own risk :)