/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.awt; import javax.swing.event.*; import javax.swing.undo.*; import org.openide.util.enums.*; import org.openide.util.RequestProcessor; import org.openide.util.Task; /** Undo and Redo manager for top components and workspace elements. * It allows <code>UndoAction</code> and <code>RedoAction</code> to listen to editing changes of active * components and to changes in their ability to do undo and redo. * * @see org.openide.actions.UndoAction * @see org.openide.actions.RedoAction * @see org.openide.windows.TopComponent#getUndoRedo * * @author Jaroslav Tulach */ public interface UndoRedo { /** Empty implementation that does not allow * any undo or redo actions. */ public static final UndoRedo NONE = new Empty (); /** Test whether the component currently has edits which may be undone. * @return <code>true</code> if undo is allowed */ public boolean canUndo (); /** Test whether the component currently has undone edits which may be redone. * @return <code>true</code> if redo is allowed */ public boolean canRedo (); /** Undo an edit. * @exception CannotUndoException if it fails */ public void undo () throws CannotUndoException; /** Redo a previously undone edit. * @exception CannotRedoException if it fails */ public void redo () throws CannotRedoException; /** Add a change listener. * The listener will be notified every time the undo/redo * ability of this object changes. * @param l the listener to add */ public void addChangeListener (ChangeListener l); /** Remove a change listener. * @param l the listener to remove * @see #addChangeListener */ public void removeChangeListener (ChangeListener l); /** Get a human-presentable name describing the * undo operation. * @return the name */ public String getUndoPresentationName (); /** Get a human-presentable name describing the * redo operation. * @return the name */ public String getRedoPresentationName (); /** An undo manager which fires a change event each time it consumes a new undoable edit. */ public static class Manager extends UndoManager implements UndoRedo { /** listener list */ private EventListenerList list; /** vector of Edits to run */ private QueueEnumeration runus = new QueueEnumeration (); // for fix of #8692 /** task that clears the queue */ private Task task = Task.EMPTY; // for fix of #8692 /** private request processor */ //to solve deadlock #10826 private static RequestProcessor internalRequestProcessor = new RequestProcessor("UndoRedo Processor"); // NOI18N static final long serialVersionUID =6721367974521509720L; /** Called from undoableEditHappened() inner class */ private void superUndoableEditHappened(UndoableEditEvent ue) { super.undoableEditHappened(ue); } /** Called from discardAllEdits() inner class */ private void superDiscardAllEdits() { super.discardAllEdits(); } /** Consume an undoable edit. * Delegates to superclass and notifies listeners. * @param ue the edit */ public void undoableEditHappened (final UndoableEditEvent ue) { /* Edits are posted to request processor and the deadlock * in #8692 between undoredo and document that fires * the undoable edit should be avoided this way. */ runus.put (ue); updateTask(); } /** Discard all the existing edits from the undomanager. */ public void discardAllEdits() { runus.put ((Object)null); updateTask(); } public boolean canUndo () { /* First it must be checked that there are * undoable edits waiting to be added to undoredo. */ if (runus.hasMoreElements()) { task.waitFinished (); } return super.canUndo (); } private void fireChange() { if (list == null) return; Object[] l = list.getListenerList (); if (l.length == 0) return; ChangeEvent ev = new ChangeEvent (this); for (int i = l.length - 1; i >= 0; i -= 2) { ((ChangeListener)l[i]).stateChanged (ev); } } private void updateTask() { /* The following task is finished when there are no * undoable edits waiting to be added to undoredo. */ //Use internal not default RequestProcessor to solve deadlock #10826 task = internalRequestProcessor.post (new Runnable () { public void run () { while (runus.hasMoreElements ()) { UndoableEditEvent ue = (UndoableEditEvent)runus.nextElement (); if (ue == null) { superDiscardAllEdits(); } else { superUndoableEditHappened (ue); } fireChange(); } } }, 0, Thread.MAX_PRIORITY); } /* Attaches change listener to the this object. * The listener is notified everytime the undo/redo * ability of this object changes. */ public synchronized void addChangeListener (ChangeListener l) { if (list == null) { list = new EventListenerList (); } list.add (ChangeListener.class, l); } /* Removes the listener */ public void removeChangeListener (ChangeListener l) { if (list != null) { list.remove (ChangeListener.class, l); } } public String getUndoPresentationName() { return this.canUndo() ? super.getUndoPresentationName() : ""; // NOI18N } public String getRedoPresentationName() { return this.canRedo() ? super.getRedoPresentationName() : ""; // NOI18N } } // XXX cannot be made private in an interface, consider removing later /** Empty implementation that does not support any undoable edits. * @deprecated Use {@link UndoRedo#NONE} rather than instantiating this. */ public static final class Empty extends Object implements UndoRedo { public boolean canUndo () { return false; } public boolean canRedo () { return false; } public void undo () throws CannotUndoException { throw new CannotUndoException (); } public void redo () throws CannotRedoException { throw new CannotRedoException (); } public void addChangeListener (ChangeListener l) { } public void removeChangeListener (ChangeListener l) { } public String getUndoPresentationName () { return ""; // NOI18N } public String getRedoPresentationName () { return ""; // NOI18N } } }