FileDocCategorySizeDatePackage
UndoManager.javaAPI DocJava SE 5 API14805Fri Aug 26 14:58:22 BST 2005javax.swing.undo

UndoManager

public class UndoManager extends CompoundEdit implements UndoableEditListener
Concrete subclass of CompoundEdit which can serve as a UndoableEditListener, consolidating the UndoableEditEvents from a variety of sources, and undoing or redoing them one at a time. Unlike AbstractUndoableEdit and CompoundEdit, the public methods of this class are synchronized, and should be safe to call from multiple threads. This should make UndoManager a convenient marshall for sets of undoable JavaBeans.

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.35, 12/19/03

Fields Summary
int
indexOfNextAdd
int
limit
Constructors Summary
public UndoManager()

        super();
        indexOfNextAdd = 0;
        limit = 100;
        edits.ensureCapacity(limit);
    
Methods Summary
public synchronized booleanaddEdit(javax.swing.undo.UndoableEdit anEdit)
If inProgress, inserts anEdit at indexOfNextAdd, and removes any old edits that were at indexOfNextAdd or later. The die method is called on each edit that is removed is sent, in the reverse of the order the edits were added. Updates indexOfNextAdd.

If not inProgress, acts as a CompoundEdit.

param
anEdit the edit to be added
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()
Overridden to preserve usual semantics: returns true if a redo operation would be successful now, false otherwise

        if (inProgress) {
            UndoableEdit edit = editToBeRedone();
            return edit != null && edit.canRedo();
        } else {
            return super.canRedo();
        }
    
public synchronized booleancanUndo()
Overridden to preserve usual semantics: returns true if an undo operation would be successful now, false otherwise

        if (inProgress) {
            UndoableEdit edit = editToBeUndone();
            return edit != null && edit.canUndo();
        } else {
            return super.canUndo();
        }
    
public synchronized booleancanUndoOrRedo()
Return true if calling undoOrRedo will undo or redo. Suitable for deciding to enable a command that toggles between the two functions, which only makes sense to use if limit == 1.

see
#undoOrRedo

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

        Enumeration cursor = edits.elements();
        while (cursor.hasMoreElements()) {
            UndoableEdit e = (UndoableEdit)cursor.nextElement();
            e.die();
        }
        edits = new Vector(limit);
        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 called. May return null

        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 called. May return null

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

        return null;
    
public synchronized voidend()
Sending end() to an UndoManager turns it into a plain old (ended) CompoundEdit.

Calls super's end() method (making inProgress false), then sends die() to the unreachable edits at indexOfNextAdd and beyond, in the reverse of the order in which they were added.

see
CompoundEdit#end

	super.end();
        this.trimEdits(indexOfNextAdd, edits.size()-1);
    
public synchronized intgetLimit()
Returns the maximum number of edits this UndoManager will hold. Default value is 100.

see
#addEdit
see
#setLimit

        return limit;
    
public synchronized java.lang.StringgetRedoPresentationName()
If inProgress, returns getRedoPresentationName of the significant edit that will be redone when redo() is invoked. If there is none, returns AbstractUndoableEdit.redoText from the defaults table.

If not inProgress, acts as a CompoundEdit

see
#redo
see
CompoundEdit#getUndoPresentationName

        if (inProgress) {
            if (canRedo()) {
                return editToBeRedone().getRedoPresentationName();
            } else {
                return UIManager.getString("AbstractUndoableEdit.redoText");
            }
        } else {
            return super.getRedoPresentationName();
        }
    
public synchronized java.lang.StringgetUndoOrRedoPresentationName()
Return the appropriate name for a command that toggles between undo and redo. Only makes sense to use such a command if limit == 1 and we're not in progress.

        if (indexOfNextAdd == edits.size()) {
            return getUndoPresentationName();
        } else {
            return getRedoPresentationName();
        }
    
public synchronized java.lang.StringgetUndoPresentationName()
If inProgress, returns getUndoPresentationName of the significant edit that will be undone when undo() is invoked. If there is none, returns AbstractUndoableEdit.undoText from the defaults table.

If not inProgress, acts as a CompoundEdit

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()
If this UndoManager is inProgress, redoes the last significant UndoableEdit at indexOfNextAdd or after, and all insignificant edits up to it. Updates indexOfNextAdd accordingly.

If not inProgress, indexOfNextAdd is ignored and super's routine is called.

see
CompoundEdit#end

        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 indexOfNextAdd to edit. Updates indexOfNextAdd accordingly.

        boolean done = false;
        while (!done) {
            UndoableEdit next = (UndoableEdit)edits.elementAt(indexOfNextAdd++);
            next.redo();
            done = next == edit;
        }
    
public synchronized voidsetLimit(int l)
Set the maximum number of edits this UndoManager will hold. If edits need to be discarded to shrink the limit, they will be told to die in the reverse of the order that they were added.

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)
Tell the edits in the given range (inclusive) to die, and remove them from edits. from > to is a no-op.

        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()
Reduce the number of queued edits to a range of size limit, centered on indexOfNextAdd.

        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()
If this UndoManager is inProgress, undo the last significant UndoableEdit before indexOfNextAdd, and all insignificant edits back to it. Updates indexOfNextAdd accordingly.

If not inProgress, indexOfNextAdd is ignored and super's routine is called.

see
CompoundEdit#end

        if (inProgress) {
            UndoableEdit edit = editToBeUndone();
            if (edit == null) {
                throw new CannotUndoException();
            }
            undoTo(edit);
        } else {
            super.undo();
        }
    
public synchronized voidundoOrRedo()
Undo or redo as appropriate. Suitable for binding to an action that toggles between these two functions. Only makes sense to send this if limit == 1.

see
#canUndoOrRedo
see
#getUndoOrRedoPresentationName

        if (indexOfNextAdd == edits.size()) {
            undo();
        } else {
            redo();
        }
    
protected voidundoTo(javax.swing.undo.UndoableEdit edit)
Undoes all changes from indexOfNextAdd to edit. Updates indexOfNextAdd accordingly.

        boolean done = false;
        while (!done) {
            UndoableEdit next = (UndoableEdit)edits.elementAt(--indexOfNextAdd);
            next.undo();
            done = next == edit;
        }
    
public voidundoableEditHappened(javax.swing.event.UndoableEditEvent e)
Called by the UndoabledEdit sources this UndoManager listens to. Calls addEdit with e.getEdit().

see
#addEdit

        addEdit(e.getEdit());