FileDocCategorySizeDatePackage
UndoManager.javaAPI DocJava SE 6 API21491Tue Jun 10 00:27:02 BST 2008javax.swing.undo

UndoManager

public class UndoManager extends CompoundEdit implements UndoableEditListener
{@code UndoManager} manages a list of {@code UndoableEdits}, providing a way to undo or redo the appropriate edits. There are two ways to add edits to an UndoManager. Add the edit directly using the addEdit method, or add the UndoManager to a bean that supports UndoableEditListener. The following examples creates an UndoManager and adds it as an UndoableEditListener to a JTextField:
UndoManager undoManager = new UndoManager();
JTextField tf = ...;
tf.getDocument().addUndoableEditListener(undoManager);

UndoManager maintains an ordered list of edits and the index of the next edit in that list. The index of the next edit is either the size of the current list of edits, or if undo has been invoked it corresponds to the index of the last significant edit that was undone. When undo is invoked all edits from the index of the next edit to the last significant edit are undone, in reverse order. For example, consider an UndoManager consisting of the following edits: A b c D. Edits with a upper-case letter in bold are significant, those in lower-case and italicized are insignificant.

Figure 1

As shown in figure 1, if D was just added, the index of the next edit will be 4. Invoking undo results in invoking undo on D and setting the index of the next edit to 3 (edit c), as shown in the following figure.

Figure 2

The last significant edit is A, so that invoking undo again invokes undo on c, b, and A, in that order, setting the index of the next edit to 0, as shown in the following figure.

Figure 3

Invoking redo results in invoking redo on all edits between the index of the next edit and the next significant edit (or the end of the list). Continuing with the previous example if redo were invoked, redo would in turn be invoked on A, b and c. In addition the index of the next edit is set to 3 (as shown in figure 2).

Adding an edit to an UndoManager results in removing all edits from the index of the next edit to the end of the list. Continuing with the previous example, if a new edit, e, is added the edit D is removed from the list (after having die invoked on it). If c is not incorporated by the next edit (c.addEdit(e) returns true), or replaced by it (e.replaceEdit(c) returns true), the new edit is added after c, as shown in the following figure.

Figure 4

Once end has been invoked on an UndoManager the superclass behavior is used for all UndoableEdit methods. Refer to CompoundEdit for more details on its behavior.

Unlike the rest of Swing, this class is thread safe.

Warning: Serialized objects of this class will not be compatible with future Swing releases. The current serialization support is appropriate for short term storage or RMI between applications running the same version of Swing. As of 1.4, support for long term storage of all JavaBeansTM has been added to the java.beans package. Please see {@link java.beans.XMLEncoder}.

author
Ray Ryan
version
1.39, 06/23/06

Fields Summary
int
indexOfNextAdd
int
limit
Constructors Summary
public UndoManager()
Creates a new UndoManager.

        super();
        indexOfNextAdd = 0;
        limit = 100;
        edits.ensureCapacity(limit);
    
Methods Summary
public synchronized booleanaddEdit(javax.swing.undo.UndoableEdit anEdit)
Adds an UndoableEdit to this UndoManager, if it's possible. This removes all edits from the index of the next edit to the end of the edits list. If end has been invoked the edit is not added and false is returned. If end hasn't been invoked this returns true.

param
anEdit the edit to be added
return
true if anEdit can be incorporated into this edit
see
CompoundEdit#end
see
CompoundEdit#addEdit

        boolean retVal;

        // Trim from the indexOfNextAdd to the end, as we'll
        // never reach these edits once the new one is added.
        trimEdits(indexOfNextAdd, edits.size()-1);

        retVal = super.addEdit(anEdit);
	if (inProgress) {
	  retVal = true;
	}

        // Maybe super added this edit, maybe it didn't (perhaps
        // an in progress compound edit took it instead. Or perhaps
        // this UndoManager is no longer in progress). So make sure
        // the indexOfNextAdd is pointed at the right place.
        indexOfNextAdd = edits.size();
        
        // Enforce the limit
        trimForLimit();

        return retVal;
    
public synchronized booleancanRedo()
Returns true if edits may be redone. If end has been invoked, this returns the value from super. Otherwise, this returns true if there are any edits to be redone (editToBeRedone returns non-null).

return
true if there are edits to be redone
see
CompoundEdit#canRedo
see
#editToBeRedone

        if (inProgress) {
            UndoableEdit edit = editToBeRedone();
            return edit != null && edit.canRedo();
        } else {
            return super.canRedo();
        }
    
public synchronized booleancanUndo()
Returns true if edits may be undone. If end has been invoked, this returns the value from super. Otherwise this returns true if there are any edits to be undone (editToBeUndone returns non-null).

return
true if there are edits to be undone
see
CompoundEdit#canUndo
see
#editToBeUndone

        if (inProgress) {
            UndoableEdit edit = editToBeUndone();
            return edit != null && edit.canUndo();
        } else {
            return super.canUndo();
        }
    
public synchronized booleancanUndoOrRedo()
Returns true if it is possible to invoke undo or redo.

return
true if invoking canUndoOrRedo is valid
see
#undoOrRedo

        if (indexOfNextAdd == edits.size()) {
            return canUndo();
        } else {
            return canRedo();
        }
    
public synchronized voiddiscardAllEdits()
Empties the undo manager sending each edit a die message in the process.

see
AbstractUndoableEdit#die

        Enumeration cursor = edits.elements();
        while (cursor.hasMoreElements()) {
            UndoableEdit e = (UndoableEdit)cursor.nextElement();
            e.die();
        }
        edits = new Vector();
        indexOfNextAdd = 0;
        // PENDING(rjrjr) when vector grows a removeRange() method
        // (expected in JDK 1.2), trimEdits() will be nice and
        // efficient, and this method can call that instead.
    
protected javax.swing.undo.UndoableEditeditToBeRedone()
Returns the the next significant edit to be redone if redo is invoked. This returns null if there are no edits to be redone.

return
the next significant edit to be redone

        int count = edits.size();
        int i = indexOfNextAdd;

        while (i < count) {
            UndoableEdit edit = (UndoableEdit)edits.elementAt(i++);
            if (edit.isSignificant()) {
                return edit;
            }
        }

        return null;
    
protected javax.swing.undo.UndoableEditeditToBeUndone()
Returns the the next significant edit to be undone if undo is invoked. This returns null if there are no edits to be undone.

return
the next significant edit to be undone

        int i = indexOfNextAdd;
        while (i > 0) {
            UndoableEdit edit = (UndoableEdit)edits.elementAt(--i);
            if (edit.isSignificant()) {
                return edit;
            }
        }

        return null;
    
public synchronized voidend()
Turns this UndoManager into a normal CompoundEdit. This removes all edits that have been undone.

see
CompoundEdit#end

	super.end();
        this.trimEdits(indexOfNextAdd, edits.size()-1);
    
public synchronized intgetLimit()
Returns the maximum number of edits this {@code UndoManager} holds. A value less than 0 indicates the number of edits is not limited.

return
the maximum number of edits this {@code UndoManager} holds
see
#addEdit
see
#setLimit

        return limit;
    
public synchronized java.lang.StringgetRedoPresentationName()
Returns a description of the redoable form of this edit. If end has been invoked this calls into super. Otherwise if there are edits to be redone, this returns the value from the next significant edit that will be redone. If there are no edits to be redone and end has not been invoked this returns the value from the UIManager property "AbstractUndoableEdit.redoText".

return
a description of the redoable form of this edit
see
#redo
see
CompoundEdit#getRedoPresentationName

        if (inProgress) {
            if (canRedo()) {
                return editToBeRedone().getRedoPresentationName();
            } else {
                return UIManager.getString("AbstractUndoableEdit.redoText");
            }
        } else {
            return super.getRedoPresentationName();
        }
    
public synchronized java.lang.StringgetUndoOrRedoPresentationName()
Convenience method that returns either getUndoPresentationName or getRedoPresentationName. If the index of the next edit equals the size of the edits list, getUndoPresentationName is returned, otherwise getRedoPresentationName is returned.

return
undo or redo name

        if (indexOfNextAdd == edits.size()) {
            return getUndoPresentationName();
        } else {
            return getRedoPresentationName();
        }
    
public synchronized java.lang.StringgetUndoPresentationName()
Returns a description of the undoable form of this edit. If end has been invoked this calls into super. Otherwise if there are edits to be undone, this returns the value from the next significant edit that will be undone. If there are no edits to be undone and end has not been invoked this returns the value from the UIManager property "AbstractUndoableEdit.undoText".

return
a description of the undoable form of this edit
see
#undo
see
CompoundEdit#getUndoPresentationName

        if (inProgress) {
            if (canUndo()) {
                return editToBeUndone().getUndoPresentationName();
            } else {
                return UIManager.getString("AbstractUndoableEdit.undoText");
            }
        } else {
            return super.getUndoPresentationName();
        }
    
public synchronized voidredo()
Redoes the appropriate edits. If end has been invoked this calls through to the superclass. Otherwise this invokes redo on all edits between the index of the next edit and the next significant edit, updating the index of the next edit appropriately.

throws
CannotRedoException if one of the edits throws CannotRedoException or there are no edits to be redone
see
CompoundEdit#end
see
#canRedo
see
#editToBeRedone

        if (inProgress) {
            UndoableEdit edit = editToBeRedone();
            if (edit == null) {
                throw new CannotRedoException();
            }
            redoTo(edit);
        } else {
            super.redo();
        }
    
protected voidredoTo(javax.swing.undo.UndoableEdit edit)
Redoes all changes from the index of the next edit to edit, updating the index of the next edit appropriately.

throws
CannotRedoException if one of the edits throws CannotRedoException

        boolean done = false;
        while (!done) {
            UndoableEdit next = (UndoableEdit)edits.elementAt(indexOfNextAdd++);
            next.redo();
            done = next == edit;
        }
    
public synchronized voidsetLimit(int l)
Sets the maximum number of edits this UndoManager holds. A value less than 0 indicates the number of edits is not limited. If edits need to be discarded to shrink the limit, die will be invoked on them in the reverse order they were added. The default is 100.

param
l the new limit
throws
RuntimeException if this {@code UndoManager} is not in progress ({@code end} has been invoked)
see
#isInProgress
see
#end
see
#addEdit
see
#getLimit

        if (!inProgress) throw new RuntimeException("Attempt to call UndoManager.setLimit() after UndoManager.end() has been called");
        limit = l;
        trimForLimit();
    
public java.lang.StringtoString()
Returns a string that displays and identifies this object's properties.

return
a String representation of this object

        return super.toString() + " limit: " + limit + 
            " indexOfNextAdd: " + indexOfNextAdd;
    
protected voidtrimEdits(int from, int to)
Removes edits in the specified range. All edits in the given range (inclusive, and in reverse order) will have die invoked on them and are removed from the list of edits. This has no effect if from > to.

param
from the minimum index to remove
param
to the maximum index to remove

        if (from <= to) {
//          System.out.println("Trimming " + from + " " + to + " with index " +
//                           indexOfNextAdd);
            for (int i = to; from <= i; i--) {
                UndoableEdit e = (UndoableEdit)edits.elementAt(i);
//              System.out.println("JUM: Discarding " +
//                                 e.getUndoPresentationName());
                e.die();
                // PENDING(rjrjr) when Vector supports range deletion (JDK
                // 1.2) , we can optimize the next line considerably. 
                edits.removeElementAt(i);
            }

            if (indexOfNextAdd > to) {
//              System.out.print("...right...");
                indexOfNextAdd -= to-from+1;
            } else if (indexOfNextAdd >= from) {
//              System.out.println("...mid...");
                indexOfNextAdd = from;
            }

//          System.out.println("new index " + indexOfNextAdd);
        }
    
protected voidtrimForLimit()
Reduces the number of queued edits to a range of size limit, centered on the index of the next edit.

        if (limit >= 0) {
            int size = edits.size();
//          System.out.print("limit: " + limit +
//                           " size: " + size +
//                           " indexOfNextAdd: " + indexOfNextAdd +
//                           "\n");
        
            if (size > limit) {
                int halfLimit = limit/2;
                int keepFrom = indexOfNextAdd - 1 - halfLimit;
                int keepTo   = indexOfNextAdd - 1 + halfLimit;

                // These are ints we're playing with, so dividing by two
                // rounds down for odd numbers, so make sure the limit was
                // honored properly. Note that the keep range is
                // inclusive.

                if (keepTo - keepFrom + 1 > limit) {
                    keepFrom++;
                }

                // The keep range is centered on indexOfNextAdd,
                // but odds are good that the actual edits Vector
                // isn't. Move the keep range to keep it legal.

                if (keepFrom < 0) {
                    keepTo -= keepFrom;
                    keepFrom = 0;
                }
                if (keepTo >= size) {
                    int delta = size - keepTo - 1;
                    keepTo += delta;
                    keepFrom += delta;
                }

//              System.out.println("Keeping " + keepFrom + " " + keepTo);
                trimEdits(keepTo+1, size-1);
                trimEdits(0, keepFrom-1);
            }
        }
    
public synchronized voidundo()
Undoes the appropriate edits. If end has been invoked this calls through to the superclass, otherwise this invokes undo on all edits between the index of the next edit and the last significant edit, updating the index of the next edit appropriately.

throws
CannotUndoException if one of the edits throws CannotUndoException or there are no edits to be undone
see
CompoundEdit#end
see
#canUndo
see
#editToBeUndone

        if (inProgress) {
            UndoableEdit edit = editToBeUndone();
            if (edit == null) {
                throw new CannotUndoException();
            }
            undoTo(edit);
        } else {
            super.undo();
        }
    
public synchronized voidundoOrRedo()
Convenience method that invokes one of undo or redo. If any edits have been undone (the index of the next edit is less than the length of the edits list) this invokes redo, otherwise it invokes undo.

see
#canUndoOrRedo
see
#getUndoOrRedoPresentationName
throws
CannotUndoException if one of the edits throws CannotUndoException
throws
CannotRedoException if one of the edits throws CannotRedoException

        if (indexOfNextAdd == edits.size()) {
            undo();
        } else {
            redo();
        }
    
protected voidundoTo(javax.swing.undo.UndoableEdit edit)
Undoes all changes from the index of the next edit to edit, updating the index of the next edit appropriately.

throws
CannotUndoException if one of the edits throws CannotUndoException

        boolean done = false;
        while (!done) {
            UndoableEdit next = (UndoableEdit)edits.elementAt(--indexOfNextAdd);
            next.undo();
            done = next == edit;
        }
    
public voidundoableEditHappened(javax.swing.event.UndoableEditEvent e)
An UndoableEditListener method. This invokes addEdit with e.getEdit().

param
e the UndoableEditEvent the UndoableEditEvent will be added from
see
#addEdit

        addEdit(e.getEdit());