How To Provide More Informative Undo Presentation Names for Document Edits

Hello,

Hopefully, I can state this clearly enough where people will know what I'm talking about.

I'm working on an app that makes use of JTextArea to provide display and editing capabilities for text files. I've registered an UndoableEditListener with the Document underlying the JTextArea, so that I can provide undo/redo capability for all the various edits.

Here's my problem, the edits sent to my listener are of the type AbstractDocument.DefaultDocumentEvent, and so they provide very little information about exactly what sort of event has taken place. IIRC, everything (cut, paste, typing, etc.) shows up as an insert or a removal. I'd like to be able to provide a better presentation name (e.g, "Undo Cut", "Undo Paste", etc.) for my undoable events than is provided by the AbstractDocument.DefaultDocumentEvent , but I'm not sure how exactly to go about this. Do I create my own edits that operate directly on the Document, and avoid the AbstractDocument.DefaultDocumentEvent altogether, or is there some easier way? Anyone have any experience trying to do this? Thanks.

- sixtyten

[1128 byte] By [sixtytena] at [2007-11-27 8:11:53]
# 1

The javax.swing.undo package actually provides a fairly neat way to do what you're talking about: create a CompoundEdit with the desired presentation name, perform the action, intercept the resulting "native" UndoableEdit (the DefaultDocumentEvent, in this case) and add it to the CompoundEdit, then end() the CompoundEdit and add it to the UndoManager. That takes care of another problem as well, i.e., user actions that generate more than one native UndoableEdit. For instance, if you select some text, then paste or type something in its place, two events are generated: a "remove" followed by an "insert". With the intercept-and-wrap techinque, both edits will be undone at once, which I consider to be the correct behavior.

The interception and wrapping is straightforward, but you still have to notify the UndoManager when each action starts and ends. To do that without creating too much coupling between subsystems... sounds like fun. ^_^

uncle_alicea at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 2

uncle_alice,

Thanks for your reply. That sounds like a pretty reasonable and straightforward way to approach it. I'll give it a go. One thing, though.Do I need to worry about any sort of threading stuff with this approach? Probably unlikely, but if two threads tried to perform some sort of edit on the text area at once, is it possible that you'd end up with a race condition? Again, seems unlikely, so maybe I'll just go ahead, and deal with threading issues if they come up. Thanks again.

- sixtyten

sixtytena at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 3
Well, when it comes to Swing and threads, all code has to be considered guilty until proven innocent, but I don't see why you couldn't implememt my suggestion in a thread-safe manner. Then again, I don't see why you would need to, either. ^_^
uncle_alicea at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 4

> The interception and wrapping is straightforward,

I know I didn't ask the question, but I must be missing something here, I do not see how this is straight forward.

How do you differentiate an UndoableEdit that was generated as a result of a "copy" or "paste" versus the simple "typing of a character"?

Where do you intercept this?

For what its worth I know how to combine multipe edits into a single undoable edit as is demonstrated by my code in this posting:

http://forum.java.sun.com/thread.jspa?forumID=57&threadID=637225

But by the time the edit gets to this code I only know if its an insert or a deletion. I have no idea whether the insertion was as a result of a "copy", "paste" or "typing".

camickra at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 5

That's exactly the problem: the information we need isn't available, and there's no way to reconstruct it, so let's have the client tell us what we need to know. What I'm proposing is an UndoManager that requires its client to notify it when an action is about to be performed, and again when the action is finished. Upon receiving the first notification, it creates a CompoundEdit with the presentation name that was provided in the notification. After that, any edits that come in via the undoableEditHappened() method get added to the CompoundEdit instead of directly to the undo stack. When it receives the second notification, the UndoManager ends the CompoundEdit and adds it to the undo stack. Here's a very simplistic example: public class SuperUndoManager extends UndoManager

{

// basically just a CompoundEdit with a 'name' attribute.

private NamedCompoundEdit currentEdit;

public void beginCompoundEdit(String name)

{

currentEdit = new NamedCompoundEdit(name);

}

public void endCompoundEdit()

{

currentEdit.end();

addEdit(currentEdit);

}

public void undoableEditHappened(UndoableEditEvent evt)

{

currentEdit.addEdit(evt.getEdit());

}

}

The biggest problem is getting the clients (i.e., JTextComponents and the Actions defined in their EditorKits) to call these methods without having to do something drastic like replacing them all. I think it can be done (he said with a barely noticeable quaver in his voice).

uncle_alicea at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 6

> The biggest problem is getting the clients (i.e., JTextComponents and

> the Actions defined in their EditorKits) to call these methods without

> having to do something drastic like replacing them all.

Agreed, which is why the word straightforward confused me. The concept is staightforward, however the implementation isn't.

camickra at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 7
Now you're confusing me. The straightforward part is what I just showed. Getting the client to provide the notifications is where it gets interesting.
uncle_alicea at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...
# 8
> Getting the client to provide the notifications is where it gets interesting. Thats what I meant as well. Implementing the whole concept.
camickra at 2007-7-12 19:56:06 > top of Java-index,Desktop,Core GUI APIs...