diff options
Diffstat (limited to 'libjava/classpath/javax/swing/undo')
-rw-r--r-- | libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java | 323 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/CannotRedoException.java | 56 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/CannotUndoException.java | 58 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/CompoundEdit.java | 400 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/StateEdit.java | 265 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/StateEditable.java | 114 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/UndoManager.java | 625 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/UndoableEdit.java | 157 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/UndoableEditSupport.java | 272 | ||||
-rw-r--r-- | libjava/classpath/javax/swing/undo/package.html | 46 |
10 files changed, 2316 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java b/libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java new file mode 100644 index 000000000..fc586604a --- /dev/null +++ b/libjava/classpath/javax/swing/undo/AbstractUndoableEdit.java @@ -0,0 +1,323 @@ +/* AbstractUndoableEdit.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.undo; + +import java.io.Serializable; + +import javax.swing.UIManager; + +/** + * A default implementation of <code>UndoableEdit</code> that can be + * used as a base for implementing editing operations. + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class AbstractUndoableEdit + implements UndoableEdit, Serializable +{ + /** + * The serialization ID. Verified using the <code>serialver</code> + * tool of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, and Sun JDK + * 1.4.1_01 on GNU/Linux. + */ + static final long serialVersionUID = 580150227676302096L; + + + /** + * The constant string “Undo”, which was returned by + * {@link #getUndoPresentationName()} on early versions of the + * platform. However, this field has become obsolete with version + * 1.3.1. That method now retrieves a localized string from the + * {@link javax.swing.UIManager}, using the key + * <code>“AbstractUndoableEdit.undoText”</code>. + */ + protected static final String UndoName = "Undo"; + + + /** + * The constant string “Redo”, which was returned by + * {@link #getRedoPresentationName()} on early versions of the + * platform. However, this field has become obsolete with version + * 1.3.1. That method now retrieves a localized string from the + * {@link javax.swing.UIManager}, using the key + * <code>“AbstractUndoableEdit.redoText”</code>. + */ + protected static final String RedoName = "Redo"; + + + /** + * Indicates whether this editing action has been executed. A value + * of <code>true</code> means that the action was performed, or that + * a redo operation was successful. A value of <code>false</code> + * means that the action has not yet performed, or that an undo + * operation was successful. + */ + private boolean hasBeenDone; + + + /** + * Indicates whether this editing action is still alive. The value + * is set to <code>true</code> by the constructor, and to + * <code>false</code> by the {@link #die()} method. + */ + private boolean alive; + + + /** + * Constructs a new <code>AbstractUndoableEdit</code>. The initial + * state is that the editing action is alive, and + * <code>hasBeenDone</code> is <code>true</code>. + */ + public AbstractUndoableEdit() + { + // The API specification is not clear, but Mauve test code has + // determined that hasBeenDone is initially set to true. + alive = hasBeenDone = true; + } + + + /** + * Undoes this editing action. + * + * @throws CannotUndoException if {@link #canUndo()} returns + * <code>false</code>, for example because this action has already + * been undone. + * + * @see #canUndo() + * @see #redo() + */ + public void undo() + throws CannotUndoException + { + if (!canUndo()) + throw new CannotUndoException(); + hasBeenDone = false; + } + + + /** + * Determines whether it would be possible to undo this editing + * action. + * + * @return <code>true</code> to indicate that this action can be + * undone, <code>false</code> otherwise. + * + * @see #undo() + * @see #canRedo() + */ + public boolean canUndo() + { + return alive && hasBeenDone; + } + + + /** + * Redoes this editing action. + * + * @throws CannotRedoException if {@link #canRedo()} returns + * <code>false</code>, for example because this action has not + * yet been undone. + * + * @see #canRedo() + * @see #undo() + */ + public void redo() + throws CannotRedoException + { + if (!canRedo()) + throw new CannotRedoException(); + hasBeenDone = true; + } + + + /** + * Determines whether it would be possible to redo this editing + * action. + * + * @return <code>true</code> to indicate that this action can be + * redone, <code>false</code> otherwise. + * + * @see #redo() + * @see #canUndo() + */ + public boolean canRedo() + { + return alive && !hasBeenDone; + } + + + /** + * Informs this edit action that it will no longer be used. Some + * actions might use this information to release resources, for + * example open files. Called by {@link UndoManager} before this + * action is removed from the edit queue. + */ + public void die() + { + alive = false; + } + + + /** + * Incorporates another editing action into this one, thus forming a + * combined action. + * + * <p>The default implementation always returns <code>false</code>, + * indicating that the editing action could not be incorporated. + * + * @param edit the editing action to be incorporated. + */ + public boolean addEdit(UndoableEdit edit) + { + return false; + } + + + /** + * Incorporates another editing action into this one, thus forming a + * combined action that replaces the argument action. + * + * <p>The default implementation always returns <code>false</code>, + * indicating that the argument action should not be replaced. + * + * @param edit the editing action to be replaced. + */ + public boolean replaceEdit(UndoableEdit edit) + { + return false; + } + + + /** + * Determines whether this editing action is significant enough for + * being seperately undoable by the user. A typical significant + * action would be the resizing of an object. However, changing the + * selection in a text document would usually not be considered + * significant. + * + * <p>The default implementation returns <code>true</code>. + * + * @return <code>true</code> to indicate that the action is + * significant enough for being separately undoable, or + * <code>false</code> otherwise. + */ + public boolean isSignificant() + { + return true; + } + + + /** + * Returns a human-readable, localized name that describes this + * editing action and can be displayed to the user. + * + * <p>The default implementation returns an empty string. + */ + public String getPresentationName() + { + return ""; + } + + + /** + * Calculates a localized name for presenting the undo action to the + * user. + * + * <p>The default implementation returns the concatenation of the + * string “Undo” and the action name, which is + * determined by calling {@link #getPresentationName()}. + * + * <p>The string “Undo” is retrieved from the {@link + * javax.swing.UIManager}, using the key + * <code>“AbstractUndoableEdit.undoText”</code>. This + * allows the text to be localized. + */ + public String getUndoPresentationName() + { + String msg, pres; + + msg = UIManager.getString("AbstractUndoableEdit.undoText"); + if (msg == null) + msg = UndoName; + + pres = getPresentationName(); + if ((pres == null) || (pres.length() == 0)) + return msg; + else + return msg + ' ' + pres; + } + + + /** + * Calculates a localized name for presenting the redo action to the + * user. + * + * <p>The default implementation returns the concatenation of the + * string “Redo” and the action name, which is + * determined by calling {@link #getPresentationName()}. + * + * <p>The string “Redo” is retrieved from the {@link + * javax.swing.UIManager}, using the key + * <code>“AbstractUndoableEdit.redoText”</code>. This + * allows the text to be localized. + */ + public String getRedoPresentationName() + { + String msg, pres; + + msg = UIManager.getString("AbstractUndoableEdit.redoText"); + if (msg == null) + msg = RedoName; + + pres = getPresentationName(); + if ((pres == null) || (pres.length() == 0)) + return msg; + else + return msg + ' ' + pres; + } + + + public String toString() + { + return super.toString() + + " hasBeenDone: " + hasBeenDone + + " alive: " + alive; + } +} diff --git a/libjava/classpath/javax/swing/undo/CannotRedoException.java b/libjava/classpath/javax/swing/undo/CannotRedoException.java new file mode 100644 index 000000000..5f2264835 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/CannotRedoException.java @@ -0,0 +1,56 @@ +/* CannotRedoException.java + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.undo; + +/** + * An exception which indicates that an editing action cannot be + * redone. + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class CannotRedoException extends RuntimeException +{ + /** + * Constructs a new instance of a <code>CannotRedoException</code>. + */ + public CannotRedoException() + { + super(); + } +} diff --git a/libjava/classpath/javax/swing/undo/CannotUndoException.java b/libjava/classpath/javax/swing/undo/CannotUndoException.java new file mode 100644 index 000000000..8d08cda68 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/CannotUndoException.java @@ -0,0 +1,58 @@ +/* CannotUndoException.java + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.undo; + + +/** + * An exception which indicates that an editing action cannot be + * undone. + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class CannotUndoException + extends RuntimeException +{ + /** + * Constructs a new instance of a <code>CannotUndoException</code>. + */ + public CannotUndoException() + { + super(); + } +} diff --git a/libjava/classpath/javax/swing/undo/CompoundEdit.java b/libjava/classpath/javax/swing/undo/CompoundEdit.java new file mode 100644 index 000000000..64e51740d --- /dev/null +++ b/libjava/classpath/javax/swing/undo/CompoundEdit.java @@ -0,0 +1,400 @@ +/* CompoundEdit.java -- Combines multiple UndoableEdits. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.undo; + +import java.util.Vector; + +/** + * An editing action that consists of multiple + * <code>UndoableEdits</code>. + * + * <p>The use of a <code>CompoundEdit</code> is divided in two separate + * phases.</p> + * + * <ol> + * <li>In the first phase, the <code>CompoundEdit</code> is + * initialized. After a new instance of <code>CompoundEdit</code> has + * been created, {@link #addEdit(UndoableEdit)} is called for each + * element of the compound. To terminate the initialization phase, + * call {@link #end()}.</li> + * <li>In the second phase, the the <code>CompoundEdit</code> can be + * used, typically by invoking {@link #undo()} and + * {@link #redo()}.</li> + * </ol> + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class CompoundEdit + extends AbstractUndoableEdit +{ + /** + * The identifier of this class in object serialization. Determined + * using the serialver tool of Sun J2SE 1.4.1_01. + */ + private static final long serialVersionUID = -6512679249930119683L; + + + /** + * The <code>UndoableEdit</code>s being combined into a compound + * editing action. + */ + protected Vector<UndoableEdit> edits; + + + /** + * Indicates whether the creation of this CompoundEdit is still in + * progress. Initially, the value of this flag is + * <code>true</code>. The {@link #end()} method changes the flag to + * <code>false</code>. + */ + private boolean inProgress; + + + /** + * Constructs a new CompoundEdit. + */ + public CompoundEdit() + { + edits = new Vector<UndoableEdit>(); + inProgress = true; + } + + + /** + * Undoes all edits that are part of of this + * <code>CompoundEdit</code>. The compound elements will receive the + * <code>undo</code> message in the reverse order of addition. + * + * @throws CannotUndoException if {@link #canUndo()} returns + * <code>false</code>. This can happen if {@link #end()} has not + * been called on this <code>CompoundEdit</code>, or if this edit + * has already been undone. + * + * @see #canUndo() + * @see #redo() + */ + public void undo() + throws CannotUndoException + { + // AbstractUndoableEdit.undo() will throw a CannotUndoException if + // canUndo returns false. + super.undo(); + + for (int i = edits.size() - 1; i >= 0; i--) + edits.elementAt(i).undo(); + } + + + /** + * Redoes all edits that are part of of this + * <code>CompoundEdit</code>. The compound elements will receive the + * <code>undo</code> message in the same order as they were added. + * + * @throws CannotRedoException if {@link #canRedo()} returns + * <code>false</code>. This can happen if {@link #end()} has not + * been called on this <code>CompoundEdit</code>, or if this edit + * has already been redone. + * + * @see #canRedo() + * @see #undo() + */ + public void redo() + throws CannotRedoException + { + // AbstractUndoableEdit.redo() will throw a CannotRedoException if + // canRedo returns false. + super.redo(); + + for (int i = 0; i < edits.size(); i++) + edits.elementAt(i).redo(); + } + + + /** + * Returns the the <code>UndoableEdit</code> that was last added to + * this compound. + */ + protected UndoableEdit lastEdit() + { + if (edits.size() == 0) + return null; + else + return edits.elementAt(edits.size() - 1); + } + + + /** + * Informs this edit action, and all compound edits, that they will + * no longer be used. Some actions might use this information to + * release resources such as open files. Called by {@link + * UndoManager} before this action is removed from the edit queue. + * + * <p>The compound elements will receive the + * <code>die</code> message in the reverse order of addition. + */ + public void die() + { + for (int i = edits.size() - 1; i >= 0; i--) + edits.elementAt(i).die(); + + super.die(); + } + + + /** + * Incorporates another editing action into this one, thus forming a + * combined edit. + * + * <p>If this edit’s {@link #end()} method has been called + * before, <code>false</code> is returned immediately. Otherwise, + * the {@linkplain #lastEdit() last added edit} is given the + * opportunity to {@linkplain UndoableEdit#addEdit(UndoableEdit) + * incorporate} <code>edit</code>. If this fails, <code>edit</code> + * is given the opportunity to {@linkplain + * UndoableEdit#replaceEdit(UndoableEdit) replace} the last added + * edit. If this fails as well, <code>edit</code> gets added as a + * new compound to {@link #edits}. + * + * @param edit the editing action being added. + * + * @return <code>true</code> if <code>edit</code> could somehow be + * incorporated; <code>false</code> if <code>edit</code> has not + * been incorporated because {@link #end()} was called before. + */ + public boolean addEdit(UndoableEdit edit) + { + UndoableEdit last; + + // If end has been called before, do nothing. + if (!inProgress) + return false; + + last = lastEdit(); + + // If edit is the very first edit, just add it to the list. + if (last == null) + { + edits.add(edit); + return true; + } + + // Try to incorporate edit into last. + if (last.addEdit(edit)) + return true; + + // Try to replace last by edit. + if (edit.replaceEdit(last)) + { + edits.set(edits.size() - 1, edit); + return true; + } + + // If everything else has failed, add edit to the list of compound + // edits. + edits.add(edit); + return true; + } + + + /** + * Informs this <code>CompoundEdit</code> that its construction + * phase has been completed. After this method has been called, + * {@link #undo()} and {@link #redo()} may be called, {@link + * #isInProgress()} will return <code>false</code>, and all attempts + * to {@linkplain #addEdit(UndoableEdit) add further edits} will + * fail. + */ + public void end() + { + inProgress = false; + } + + + /** + * Determines whether it would be possible to undo this editing + * action. The result will be <code>true</code> if {@link #end()} + * has been called on this <code>CompoundEdit</code>, {@link #die()} + * has not yet been called, and the edit has not been undone + * already. + * + * @return <code>true</code> to indicate that this action can be + * undone; <code>false</code> otherwise. + * + * @see #undo() + * @see #canRedo() + */ + public boolean canUndo() + { + return !inProgress && super.canUndo(); + } + + + /** + * Determines whether it would be possible to redo this editing + * action. The result will be <code>true</code> if {@link #end()} + * has been called on this <code>CompoundEdit</code>, {@link #die()} + * has not yet been called, and the edit has not been redone + * already. + * + * @return <code>true</code> to indicate that this action can be + * redone; <code>false</code> otherwise. + * + * @see #redo() + * @see #canUndo() + */ + public boolean canRedo() + { + return !inProgress && super.canRedo(); + } + + + /** + * Determines whether the initial construction phase of this + * <code>CompoundEdit</code> is still in progress. During this + * phase, edits {@linkplain #addEdit(UndoableEdit) may be + * added}. After initialization has been terminated by calling + * {@link #end()}, {@link #undo()} and {@link #redo()} can be used. + * + * @return <code>true</code> if the initialization phase is still in + * progress; <code>false</code> if {@link #end()} has been called. + * + * @see #end() + */ + public boolean isInProgress() + { + return inProgress; + } + + + /** + * Determines whether this editing action is significant enough for + * being seperately undoable by the user. A typical significant + * action would be the resizing of an object. However, changing the + * selection in a text document would usually not be considered + * significant. + * + * <p>A <code>CompoundEdit</code> is significant if any of its + * elements are significant. + */ + public boolean isSignificant() + { + for (int i = edits.size() - 1; i >= 0; i--) + if (edits.elementAt(i).isSignificant()) + return true; + + return false; + } + + + /** + * Returns a human-readable, localized name that describes this + * editing action and can be displayed to the user. + * + * <p>The implementation delegates the call to the {@linkplain + * #lastEdit() last added edit action}. If no edit has been added + * yet, the inherited implementation will be invoked, which always + * returns an empty string. + */ + public String getPresentationName() + { + UndoableEdit last; + + last = lastEdit(); + if (last == null) + return super.getPresentationName(); + else + return last.getPresentationName(); + } + + + /** + * Calculates a localized message text for presenting the undo + * action to the user. + * + * <p>The implementation delegates the call to the {@linkplain + * #lastEdit() last added edit action}. If no edit has been added + * yet, the {@linkplain + * AbstractUndoableEdit#getUndoPresentationName() inherited + * implementation} will be invoked. + */ + public String getUndoPresentationName() + { + UndoableEdit last; + + last = lastEdit(); + if (last == null) + return super.getUndoPresentationName(); + else + return last.getUndoPresentationName(); + } + + + /** + * Calculates a localized message text for presenting the redo + * action to the user. + * + * <p>The implementation delegates the call to the {@linkplain + * #lastEdit() last added edit action}. If no edit has been added + * yet, the {@linkplain + * AbstractUndoableEdit#getRedoPresentationName() inherited + * implementation} will be invoked. + */ + public String getRedoPresentationName() + { + UndoableEdit last; + + last = lastEdit(); + if (last == null) + return super.getRedoPresentationName(); + else + return last.getRedoPresentationName(); + } + + + /** + * Calculates a string that may be useful for debugging. + */ + public String toString() + { + return super.toString() + + " inProgress: " + inProgress + + " edits: " + edits; + } +} diff --git a/libjava/classpath/javax/swing/undo/StateEdit.java b/libjava/classpath/javax/swing/undo/StateEdit.java new file mode 100644 index 000000000..819d052c5 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/StateEdit.java @@ -0,0 +1,265 @@ +/* StateEdit.java -- UndoableEdit for StateEditable implementations. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.undo; + +import java.util.Hashtable; +import java.util.Iterator; + +/** + * A helper class, making it easy to support undo and redo. + * + * <p>The following example shows how to use this class.</p> + * + * <pre> + * Foo foo; // class Foo implements {@link StateEditable} + * StateEdit edit; + * + * edit = new StateEdit(foo, "Name Change"); + * foo.setName("Jane Doe"); + * edit.end(); + * undoManager.addEdit(edit); + * </pre> + * + * <p>If <code>Foo</code>’s implementation of {@link + * StateEditable} considers the name as part of the editable state, + * the user can now choose “Undo Name Change” or + * “Redo Name Change” from the respective menu. No + * further undo support is needed from the application.</p> + * + * <p>The following explains what happens in the example.</p> + * + * <ol> + * <li>When a <code>StateEdit</code> is created, the associated + * {@link StateEditable} gets asked to store its state into a hash + * table, {@link #preState}.</li> + * <li>The application will now perform some changes to the edited + * object. This typically happens by invoking methods on the edited + * object.</li> + * <li>The editing phase is terminated by invoking the {@link #end()} + * method of the <code>StateEdit</code>. The <code>end()</code> method + * does two things. + * + * <ul> + * <li>The edited object receives a second request for storing + * its state. This time, it will use a different hash table, {@link + * #postState}.</li> + * <li>To increase efficiency, the <code>StateEdit</code> now removes + * any entries from {@link #preState} and {@link #postState} that have + * the same key, and whose values are equal. Equality is determined + * by invoking the <code>equals</code> method inherited from + * {@link java.lang.Object}.</li> + * </ul></li> + * <li>When the user later chooses to undo the <code>StateEdit</code>, + * the edited object is asked to {@linkplain StateEditable#restoreState + * restore its state} from the {@link #preState} table. Similarly, + * when the user chooses to <i>redo</i> the <code>StateEdit</code>, + * the edited object gets asked to restore its state from the {@link + * #postState}.</li> + * </ol> + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class StateEdit + extends AbstractUndoableEdit +{ + /** + * The ID of the Java source file in Sun’s Revision Control + * System (RCS). This certainly should not be part of the API + * specification. But in order to be API-compatible with + * Sun’s reference implementation, GNU Classpath also has to + * provide this field and match its value. The value used here has + * been in every JDK release at least from 1.2 to 1.5. + */ + protected static final String RCSID = "$" + + "Id: StateEdit.java,v 1.6 1997/10/01 20:05:51 sandipc Exp $"; + + + /** + * The object which is being edited by this <code>StateEdit</code>. + */ + protected StateEditable object; + + + /** + * The state of <code>object</code> at the time of constructing + * this <code>StateEdit</code>. + */ + protected Hashtable<Object, Object> preState; + + + /** + * The state of <code>object</code> at the time when {@link #end()} + * was called. + */ + protected Hashtable<Object, Object> postState; + + + /** + * A human-readable name for this edit action. + */ + protected String undoRedoName; + + + /** + * Constructs a <code>StateEdit</code>, specifying the object whose + * state is being edited. + * + * @param obj the object whose state is being edited by this + * <code>StateEdit</code>. + */ + public StateEdit(StateEditable obj) + { + init(obj, null); + } + + + /** + * Constructs a <code>StateEdit</code>, specifying the object whose + * state is being edited. + * + * @param obj the object whose state is being edited by this + * <code>StateEdit</code>. + * + * @param name the human-readable name of the editing action. + */ + public StateEdit(StateEditable obj, String name) + { + init(obj, name); + } + + + /** + * Initializes this <code>StateEdit</code>. The edited object will + * be asked to store its current state into {@link #preState}. + * + * @param obj the object being edited. + * + * @param name the human-readable name of the editing action. + */ + protected void init(StateEditable obj, String name) + { + object = obj; + undoRedoName = name; + preState = new Hashtable<Object,Object>(); + postState = new Hashtable<Object,Object>(); + obj.storeState(preState); + } + + + /** + * Informs this <code>StateEdit</code> that all edits are finished. + * The edited object will be asked to store its state into {@link + * #postState}, and any redundant entries will get removed from + * {@link #preState} and {@link #postState}. + */ + public void end() + { + object.storeState(postState); + removeRedundantState(); + } + + + /** + * Undoes this edit operation. The edited object will be asked to + * {@linkplain StateEditable#restoreState restore its state} from + * {@link #preState}. + * + * @throws CannotUndoException if {@link #canUndo()} returns + * <code>false</code>, for example because this action has already + * been undone. + */ + public void undo() + { + super.undo(); + object.restoreState(preState); + } + + + /** + * Redoes this edit operation. The edited object will be asked to + * {@linkplain StateEditable#restoreState restore its state} from + * {@link #postState}. + * + * @throws CannotRedoException if {@link #canRedo()} returns + * <code>false</code>, for example because this action has not yet + * been undone. + */ + public void redo() + { + super.redo(); + object.restoreState(postState); + } + + + /** + * Returns a human-readable, localized name that describes this + * editing action and can be displayed to the user. + * + * @return the name, or <code>null</code> if no presentation + * name is available. + */ + public String getPresentationName() + { + return undoRedoName; + } + + + /** + * Removes all redundant entries from the pre- and post-edit state + * hash tables. An entry is considered redundant if it is present + * both before and after the edit, and if the two values are equal. + */ + protected void removeRedundantState() + { + Iterator<Object> i = preState.keySet().iterator(); + while (i.hasNext()) + { + Object key = i.next(); + if (postState.containsKey(key)) + { + if (preState.get(key).equals(postState.get(key))) + { + i.remove(); + postState.remove(key); + } + } + } + } +} diff --git a/libjava/classpath/javax/swing/undo/StateEditable.java b/libjava/classpath/javax/swing/undo/StateEditable.java new file mode 100644 index 000000000..7e6cc9785 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/StateEditable.java @@ -0,0 +1,114 @@ +/* StateEditable.java -- Interface for collaborating with StateEdit. + Copyright (C) 2002, 2003, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.undo; + +import java.util.Hashtable; + + +/** + * The interface for objects whose state can be undone or redone by a + * {@link StateEdit} action. + * + * <p>The following example shows how to write a class that implements + * this interface. + * + * <pre> class Foo + * implements StateEditable + * { + * private String name; + * + * public void setName(String n) { name = n; } + * + * public void restoreState(Hashtable h) + * { + * if (h.containsKey("name")) + * setName((String) h.get("name")); + * } + * + * public void storeState(Hashtable s) + * { + * s.put("name", name); + * } + * }</pre> + * + * @see StateEdit + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public interface StateEditable +{ + /** + * The ID of the Java source file in Sun’s Revision Control + * System (RCS). This certainly should not be part of the API + * specification. But in order to be API-compatible with + * Sun’s reference implementation, GNU Classpath also has to + * provide this field and match its value. The value used here has + * been in every JDK release at least from 1.2 to 1.5. + */ + String RCSID = "$" + + "Id: StateEditable.java,v 1.2 1997/09/08 19:39:08 marklin Exp $"; + + + /** + * Performs an edit action, taking any editable state information + * from the specified hash table. + * + * <p><b>Note to implementors of this interface:</b> To increase + * efficiency, the <code>StateEdit</code> class {@link + * StateEdit#removeRedundantState()} removes redundant state + * information. Therefore, implementations of this interface must be + * prepared for the case where certain keys were stored into the + * table by {@link #storeState}, but are not present anymore + * when the <code>restoreState</code> method gets called. + * + * @param state a hash table containing the relevant state + * information. + */ + void restoreState(Hashtable<?, ?> state); + + + /** + * Stores any editable state information into the specified hash + * table. + * + * @param state a hash table for storing relevant state + * information. + */ + void storeState(Hashtable<Object, Object> state); +} diff --git a/libjava/classpath/javax/swing/undo/UndoManager.java b/libjava/classpath/javax/swing/undo/UndoManager.java new file mode 100644 index 000000000..9b10cc575 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/UndoManager.java @@ -0,0 +1,625 @@ +/* UndoManager.java -- + Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.undo; + +import javax.swing.UIManager; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; + + +/** + * A manager for providing an application’s undo/redo + * functionality. + * + * <p>Tyipcally, an application will create only one single instance + * of UndoManager. When the user performs an undoable action, for + * instance changing the color of an object from green to blue, the + * application registers an {@link UndoableEdit} object with the + * <code>UndoManager</code>. To implement the “undo” and + * “redo” menu commands, the application invokes the + * UndoManager’s {@link #undo} and {@link #redo} methods. The + * human-readable text of these menu commands is provided by {@link + * #getUndoPresentationName} and {@link #getRedoPresentationName}, + * respectively. To determine whether the menu item should be + * selectable or greyed out, use {@link #canUndo} and {@link + * #canRedo}. + * + * <p>The UndoManager will only keep a specified number of editing + * actions, the <em>limit</em>. The value of this parameter can be + * retrieved by calling {@link #getLimit} and set with {@link + * #setLimit}. If more UndoableEdits are added to the UndoManager, + * the oldest actions will be discarded. + * + * <p>Some applications do not provide separate menu commands for + * “undo” and “redo.” Instead, they + * have just a single command whose text switches between the two. + * Such applications would use an UndoManager with a <code>limit</code> + * of 1. The text of this combined menu item is available via + * {@link #getUndoOrRedoPresentationName}, and it is implemented + * by calling {@link #undoOrRedo}. + * + * <p><b>Thread Safety:</b> In constrast to the other classes of the + * <code>javax.swing.undo</code> package, the public methods of an + * <code>UndoManager</code> are safe to call from concurrent threads. + * The caller does not need to perform external synchronization, and + * {@link javax.swing.event.UndoableEditEvent} sources do not need to + * broadcast their events from inside the Swing worker thread. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class UndoManager + extends CompoundEdit + implements UndoableEditListener +{ + /** + * The unique ID for serializing instances of this class. Determined + * using the <code>serialver</code> tool of Sun JDK 1.4.1_01 on + * GNU/Linux. + */ + static final long serialVersionUID = -2077529998244066750L; + + + /** + * An index into the inherited {@link #edits} Vector that indicates + * at which position newly added editing actions would get inserted. + * + * <p>Normally, the value of <code>indexOfNextAdd</code> equals + * the number of UndoableEdits stored by this UndoManager, i.e. + * <code>edits.size()</code>. For each call to {@link #undo}, + * <code>indexOfNextAdd</code> is decremented by one. For each + * call to {@link #redo}, it is incremented again. + */ + int indexOfNextAdd; + + + /** + * The maximum number of UndoableEdits stored by this UndoManager. + */ + int limit; + + + /** + * Constructs an UndoManager. + * + * <p>The <code>limit</code> of the freshly constructed UndoManager + * is 100. + */ + public UndoManager() + { + limit = 100; + } + + + /** + * Returns a string representation for this UndoManager. This may be + * useful for debugging purposes. For the text of menu items, please + * refer to {@link #getUndoPresentationName}, {@link + * #getRedoPresentationName}, and {@link + * #getUndoOrRedoPresentationName}. + */ + public String toString() + { + return super.toString() + + " limit: " + limit + + " indexOfNextAdd: " + indexOfNextAdd; + } + + + /** + * Puts this UndoManager into a state where it acts as a normal + * {@link CompoundEdit}. It is unlikely that an application would + * want to do this. + */ + public synchronized void end() + { + super.end(); + trimEdits(indexOfNextAdd, edits.size() - 1); + } + + + /** + * Returns how many edits this UndoManager can maximally hold. + * + * @see #setLimit + */ + public synchronized int getLimit() + { + return limit; + } + + + /** + * Changes the maximal number of edits that this UndoManager can + * process. If there are currently more edits than the new limit + * allows, they will receive a {@link UndoableEdit#die() die} + * message in reverse order of addition. + * + * @param limit the new limit. + * + * @throws IllegalStateException if {@link #end()} has already been + * called on this UndoManager. + */ + public synchronized void setLimit(int limit) + { + if (!isInProgress()) + throw new IllegalStateException(); + + this.limit = limit; + trimForLimit(); + } + + + /** + * Discards all editing actions that are currently registered with + * this UndoManager. Each {@link UndoableEdit} will receive a {@link + * UndoableEdit#die() die message}. + */ + public synchronized void discardAllEdits() + { + int size; + + size = edits.size(); + for (int i = size - 1; i >= 0; i--) + edits.get(i).die(); + indexOfNextAdd = 0; + edits.clear(); + } + + + /** + * Called by various internal methods in order to enforce + * the <code>limit</code> value. + */ + protected void trimForLimit() + { + int high, s; + + s = edits.size(); + + /* The Sun J2SE1.4.1_01 implementation can be observed to do + * nothing (instead of throwing an exception) with a negative or + * zero limit. It may be debatable whether this is the best + * behavior, but we replicate it for sake of compatibility. + */ + if (limit <= 0 || s <= limit) + return; + + high = Math.min(indexOfNextAdd + limit/2 - 1, s - 1); + trimEdits(high + 1, s - 1); + trimEdits(0, high - limit); + } + + + /** + * Discards a range of edits. All edits in the range <code>[from + * .. to]</code> will receive a {@linkplain UndoableEdit#die() die + * message} before being removed from the edits array. If + * <code>from</code> is greater than <code>to</code>, nothing + * happens. + * + * @param from the lower bound of the range of edits to be + * discarded. + * + * @param to the upper bound of the range of edits to be discarded. + */ + protected void trimEdits(int from, int to) + { + if (from > to) + return; + + for (int i = to; i >= from; i--) + edits.get(i).die(); + + // Remove the range [from .. to] from edits. If from == to, which + // is likely to be a very common case, we can do better than + // creating a sub-list and clearing it. + if (to == from) + edits.remove(from); + else + edits.subList(from, to + 1).clear(); + + if (indexOfNextAdd > to) + indexOfNextAdd = indexOfNextAdd - to + from - 1; + else if (indexOfNextAdd >= from) + indexOfNextAdd = from; + } + + + /** + * Determines which significant edit would be undone if {@link + * #undo()} was called. + * + * @return the significant edit that would be undone, or + * <code>null</code> if no significant edit would be affected by + * calling {@link #undo()}. + */ + protected UndoableEdit editToBeUndone() + { + UndoableEdit result; + + for (int i = indexOfNextAdd - 1; i >= 0; i--) + { + result = edits.get(i); + if (result.isSignificant()) + return result; + } + + return null; + } + + + /** + * Determines which significant edit would be redone if {@link + * #redo()} was called. + * + * @return the significant edit that would be redone, or + * <code>null</code> if no significant edit would be affected by + * calling {@link #redo()}. + */ + protected UndoableEdit editToBeRedone() + { + UndoableEdit result; + + for (int i = indexOfNextAdd; i < edits.size(); i++) + { + result = edits.get(i); + if (result.isSignificant()) + return result; + } + + return null; + } + + + /** + * Undoes all editing actions in reverse order of addition, + * up to the specified action, + * + * @param edit the last editing action to be undone. + */ + protected void undoTo(UndoableEdit edit) + throws CannotUndoException + { + UndoableEdit cur; + + if (!edits.contains(edit)) + throw new CannotUndoException(); + + while (true) + { + indexOfNextAdd -= 1; + cur = edits.get(indexOfNextAdd); + cur.undo(); + if (cur == edit) + return; + } + } + + + /** + * Redoes all editing actions in the same order as they were + * added to this UndoManager, up to the specified action. + * + * @param edit the last editing action to be redone. + */ + protected void redoTo(UndoableEdit edit) + throws CannotRedoException + { + UndoableEdit cur; + + if (!edits.contains(edit)) + throw new CannotRedoException(); + + while (true) + { + cur = edits.get(indexOfNextAdd); + indexOfNextAdd += 1; + cur.redo(); + if (cur == edit) + return; + } + } + + + /** + * Undoes or redoes the last action. If the last action has already + * been undone, it will be re-done, and vice versa. + * + * <p>This is useful for applications that do not present a separate + * undo and redo facility, but just have a single menu item for + * undoing and redoing the very last action. Such applications will + * use an <code>UndoManager</code> whose <code>limit</code> is 1. + */ + public synchronized void undoOrRedo() + throws CannotRedoException, CannotUndoException + { + if (indexOfNextAdd == edits.size()) + undo(); + else + redo(); + } + + + /** + * Determines whether it would be possible to either undo or redo + * this editing action. + * + * <p>This is useful for applications that do not present a separate + * undo and redo facility, but just have a single menu item for + * undoing and redoing the very last action. Such applications will + * use an <code>UndoManager</code> whose <code>limit</code> is 1. + * + * @return <code>true</code> to indicate that this action can be + * undone or redone; <code>false</code> if neither is possible at + * the current time. + */ + public synchronized boolean canUndoOrRedo() + { + return indexOfNextAdd == edits.size() ? canUndo() : canRedo(); + } + + + /** + * Undoes one significant edit action. If insignificant actions have + * been posted after the last signficant action, the insignificant + * ones will be undone first. + * + * <p>However, if {@link #end()} has been called on this + * UndoManager, it will behave like a normal {@link + * CompoundEdit}. In this case, all actions will be undone in + * reverse order of addition. Typical applications will never call + * {@link #end()} on their <code>UndoManager</code>. + * + * @throws CannotUndoException if no action can be undone. + * + * @see #canUndo() + * @see #redo() + * @see #undoOrRedo() + */ + public synchronized void undo() + throws CannotUndoException + { + if (!isInProgress()) + { + super.undo(); + return; + } + + UndoableEdit edit = editToBeUndone(); + if (edit == null) + throw new CannotUndoException(); + + undoTo(edit); + } + + + /** + * Determines whether it would be possible to undo this editing + * action. + * + * @return <code>true</code> to indicate that this action can be + * undone; <code>false</code> otherwise. + * + * @see #undo() + * @see #canRedo() + * @see #canUndoOrRedo() + */ + public synchronized boolean canUndo() + { + UndoableEdit edit; + + if (!isInProgress()) + return super.canUndo(); + + edit = editToBeUndone(); + return edit != null && edit.canUndo(); + } + + + + /** + * Redoes one significant edit action. If insignificant actions have + * been posted in between, the insignificant ones will be redone + * first. + * + * <p>However, if {@link #end()} has been called on this + * UndoManager, it will behave like a normal {@link + * CompoundEdit}. In this case, <em>all</em> actions will be redone + * in order of addition. Typical applications will never call {@link + * #end()} on their <code>UndoManager</code>. + * + * @throws CannotRedoException if no action can be redone. + * + * @see #canRedo() + * @see #redo() + * @see #undoOrRedo() + */ + public synchronized void redo() + throws CannotRedoException + { + if (!isInProgress()) + { + super.redo(); + return; + } + + UndoableEdit edit = editToBeRedone(); + if (edit == null) + throw new CannotRedoException(); + + redoTo(edit); + } + + + /** + * Determines whether it would be possible to redo this editing + * action. + * + * @return <code>true</code> to indicate that this action can be + * redone; <code>false</code> otherwise. + * + * @see #redo() + * @see #canUndo() + * @see #canUndoOrRedo() + */ + public synchronized boolean canRedo() + { + UndoableEdit edit; + + if (!isInProgress()) + return super.canRedo(); + + edit = editToBeRedone(); + return edit != null && edit.canRedo(); + } + + + /** + * Registers an undoable editing action with this UndoManager. If + * the capacity <code>limit</code> is reached, the oldest action + * will be discarded (and receives a {@linkplain UndoableEdit#die() + * die message}. Equally, any actions that were undone (but not re-done) + * will be discarded, too. + * + * @param edit the editing action that is added to this UndoManager. + * + * @return <code>true</code> if <code>edit</code> could be + * incorporated; <code>false</code> if <code>edit</code> has not + * been incorporated because {@link #end()} has already been called + * on this <code>UndoManager</code>. + */ + public synchronized boolean addEdit(UndoableEdit edit) + { + boolean result; + + // Discard any edits starting at indexOfNextAdd. + trimEdits(indexOfNextAdd, edits.size() - 1); + + result = super.addEdit(edit); + indexOfNextAdd = edits.size(); + trimForLimit(); + return result; + } + + + /** + * Calculates a localized text for presenting the undo or redo + * action to the user, for example in the form of a menu command. + * + * <p>This is useful for applications that do not present a separate + * undo and redo facility, but just have a single menu item for + * undoing and redoing the very last action. Such applications will + * use an <code>UndoManager</code> whose <code>limit</code> is 1. + * + * @return the redo presentation name if the last action has already + * been undone, or the undo presentation name otherwise. + * + * @see #getUndoPresentationName() + * @see #getRedoPresentationName() + */ + public synchronized String getUndoOrRedoPresentationName() + { + if (indexOfNextAdd == edits.size()) + return getUndoPresentationName(); + else + return getRedoPresentationName(); + } + + + /** + * Calculates a localized text for presenting the undo action + * to the user, for example in the form of a menu command. + */ + public synchronized String getUndoPresentationName() + { + UndoableEdit edit; + + if (!isInProgress()) + return super.getUndoPresentationName(); + + edit = editToBeUndone(); + if (edit == null) + return UIManager.getString("AbstractUndoableEdit.undoText"); + else + return edit.getUndoPresentationName(); + } + + + /** + * Calculates a localized text for presenting the redo action + * to the user, for example in the form of a menu command. + */ + public synchronized String getRedoPresentationName() + { + UndoableEdit edit; + + if (!isInProgress()) + return super.getRedoPresentationName(); + + edit = editToBeRedone(); + if (edit == null) + return UIManager.getString("AbstractUndoableEdit.redoText"); + else + return edit.getRedoPresentationName(); + } + + + /** + * Registers the edit action of an {@link UndoableEditEvent} + * with this UndoManager. + * + * <p><b>Thread Safety:</b> This method may safely be invoked from + * concurrent threads. The caller does not need to perform external + * synchronization. This means that {@link + * javax.swing.event.UndoableEditEvent} sources do not need to broadcast + * their events from inside the Swing worker thread. + * + * @param event the event whose <code>edit</code> will be + * passed to {@link #addEdit}. + * + * @see UndoableEditEvent#getEdit() + * @see #addEdit + */ + public void undoableEditHappened(UndoableEditEvent event) + { + // Note that this method does not need to be synchronized, + // because addEdit will obtain and release the mutex. + addEdit(event.getEdit()); + } +} diff --git a/libjava/classpath/javax/swing/undo/UndoableEdit.java b/libjava/classpath/javax/swing/undo/UndoableEdit.java new file mode 100644 index 000000000..1a08ecaa5 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/UndoableEdit.java @@ -0,0 +1,157 @@ +/* UndoableEdit.java -- + Copyright (C) 2002, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.undo; + +/** + * An editing operation that supports undo/redoability. + * + * @author Andrew Selkirk + */ +public interface UndoableEdit +{ + + /** + * Incorporates another editing action into this one, thus forming a + * combined action. + * + * @param edit the editing action to be incorporated. + * + * @return <code>true</code> if the edit was combined successfully, and + * <code>false</code> if it could not be combined. + */ + boolean addEdit(UndoableEdit edit); + + /** + * Determines whether it would be possible to redo this editing + * action. + * + * @return <code>true</code> to indicate that this action can be + * redone, <code>false</code> otherwise. + * + * @see #redo() + * @see #canUndo() + */ + boolean canRedo(); + + /** + * Determines whether it would be possible to undo this editing + * action. + * + * @return <code>true</code> to indicate that this action can be + * undone, <code>false</code> otherwise. + * + * @see #undo() + * @see #canRedo() + */ + boolean canUndo(); + + /** + * Informs this edit action that it will no longer be used. Some + * actions might use this information to release resources, for + * example open files. Called by {@link UndoManager} before this + * action is removed from the edit queue. + */ + void die(); + + /** + * Returns a human-readable, localized name that describes this + * editing action and can be displayed to the user. + * + * @return The presentation name. + */ + String getPresentationName(); + + /** + * Returns the redo presentation name. + * + * @return The redo presentation name. + */ + String getRedoPresentationName(); + + /** + * Returns the undo presentation name. + * + * @return The undo presentation name. + */ + String getUndoPresentationName(); + + /** + * Determines whether this editing action is significant enough for + * being seperately undoable by the user. A typical significant + * action would be the resizing of an object. However, changing the + * selection in a text document would usually not be considered + * significant. + * + * @return <code>true</code> to indicate that the action is + * significant enough for being separately undoable, or + * <code>false</code> otherwise. + */ + boolean isSignificant(); + + /** + * Redoes this editing action. + * + * @throws CannotRedoException if the edit cannot be undone. + * + * @see #canRedo() + * @see #undo() + */ + void redo() throws CannotRedoException; + + /** + * Incorporates another editing action into this one, thus forming a + * combined action that replaces the argument action. + * + * @param edit the editing action to be replaced. + * + * @return <code>true</code> if the edit is successfully replaced, and + * <code>false</code> otherwise. + */ + boolean replaceEdit(UndoableEdit edit); + + /** + * Undoes this editing action. + * + * @throws CannotUndoException if the edit cannot be undone. + * + * @see #canUndo() + * @see #redo() + */ + void undo() throws CannotUndoException; + +} diff --git a/libjava/classpath/javax/swing/undo/UndoableEditSupport.java b/libjava/classpath/javax/swing/undo/UndoableEditSupport.java new file mode 100644 index 000000000..2e5d90954 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/UndoableEditSupport.java @@ -0,0 +1,272 @@ +/* UndoableEditSupport.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.undo; + +import java.util.Iterator; +import java.util.Vector; + +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; + +/** + * A helper class for supporting {@link + * javax.swing.event.UndoableEditListener}. + * + * @author Andrew Selkirk (aselkirk@sympatico.ca) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class UndoableEditSupport +{ + /** + * The number of times that {@link #beginUpdate()} has been called + * without a matching call to {@link #endUpdate()}. + */ + protected int updateLevel; + + + /** + * compoundEdit + */ + protected CompoundEdit compoundEdit; + + + /** + * The currently registered listeners. + */ + protected Vector<UndoableEditListener> listeners = + new Vector<UndoableEditListener>(); + + + /** + * The source of the broadcast UndoableEditEvents. + */ + protected Object realSource; + + + /** + * Constructs a new helper for broadcasting UndoableEditEvents. The + * events will indicate the newly constructed + * <code>UndoableEditSupport</code> instance as their source. + * + * @see #UndoableEditSupport(java.lang.Object) + */ + public UndoableEditSupport() + { + realSource = this; + } + + + /** + * Constructs a new helper for broadcasting UndoableEditEvents. + * + * @param realSource the source of the UndoableEditEvents that will + * be broadcast by this helper. If <code>realSource</code> is + * <code>null</code>, the events will indicate the newly constructed + * <code>UndoableEditSupport</code> instance as their source. + */ + public UndoableEditSupport(Object realSource) + { + if (realSource == null) + realSource = this; + this.realSource = realSource; + } + + + /** + * Returns a string representation of this object that may be useful + * for debugging. + */ + public String toString() + { + // Note that often, this.realSource == this. Therefore, dumping + // realSource without additional checks may lead to infinite + // recursion. See Classpath bug #7119. + return super.toString() + " updateLevel: " + updateLevel + + " listeners: " + listeners + " compoundEdit: " + compoundEdit; + } + + + /** + * Registers a listener. + * + * @param val the listener to be added. + */ + public synchronized void addUndoableEditListener(UndoableEditListener val) + { + listeners.add(val); + } + + + /** + * Unregisters a listener. + * @param val the listener to be removed. + */ + public synchronized void removeUndoableEditListener(UndoableEditListener val) + { + listeners.removeElement(val); + } + + + /** + * Returns an array containing the currently registered listeners. + */ + public synchronized UndoableEditListener[] getUndoableEditListeners() + { + UndoableEditListener[] result = new UndoableEditListener[listeners.size()]; + return listeners.toArray(result); + } + + + /** + * Notifies all registered listeners that an {@link + * UndoableEditEvent} has occured. + * + * <p><b>Lack of Thread Safety:</b> It is <em>not</em> safe to call + * this method from concurrent threads, unless the call is protected + * by a synchronization on this <code>UndoableEditSupport</code> + * instance. + * + * @param edit the edit action to be posted. + */ + protected void _postEdit(UndoableEdit edit) + { + UndoableEditEvent event; + Iterator<UndoableEditListener> iter; + + // Do nothing if we have no listeners. + if (listeners.isEmpty()) + return; + + event = new UndoableEditEvent(realSource, edit); + + // We clone the vector because this allows listeners to register + // or unregister listeners in their undoableEditHappened method. + // Otherwise, this would throw exceptions (in the case of + // Iterator, a java.util.ConcurrentModificationException; in the + // case of a direct loop over the Vector elements, some + // index-out-of-bounds exception). + iter = new Vector<UndoableEditListener>(listeners).iterator(); + while (iter.hasNext()) + iter.next().undoableEditHappened(event); + } + + + /** + * If {@link #beginUpdate} has been called (so that the current + * update level is greater than zero), adds the specified edit + * to {@link #compoundEdit}. Otherwise, notify listeners of the + * edit by calling {@link #_postEdit(UndoableEdit)}. + * + * <p><b>Thread Safety:</b> It is safe to call this method from any + * thread without external synchronization. + * + * @param edit the edit action to be posted. + */ + public synchronized void postEdit(UndoableEdit edit) + { + if (compoundEdit != null) + compoundEdit.addEdit(edit); + else + _postEdit(edit); + } + + + /** + * Returns the current update level. + */ + public int getUpdateLevel() + { + return updateLevel; + } + + + /** + * Starts a (possibly nested) update session. If the current update + * level is zero, {@link #compoundEdit} is set to the result of the + * {@link #createCompoundEdit} method. In any case, the update level + * is increased by one. + * + * <p><b>Thread Safety:</b> It is safe to call this method from any + * thread without external synchronization. + */ + public synchronized void beginUpdate() + { + if (compoundEdit == null) + compoundEdit = createCompoundEdit(); + ++updateLevel; + } + + + /** + * Creates a new instance of {@link CompoundEdit}. Called by {@link + * #beginUpdate}. If a subclass wants {@link #beginUpdate} to work + * on a specific {@link #compoundEdit}, it should override this + * method. + * + * @return a newly created instance of {@link CompoundEdit}. + */ + protected CompoundEdit createCompoundEdit() + { + return new CompoundEdit(); + } + + + /** + * Ends an update session. If the terminated session was the + * outermost session, {@link #compoundEdit} will receive an + * <code>end</code> message, and {@link #_postEdit} gets called in + * order to notify any listeners. Finally, the + * <code>compoundEdit</code> is discarded. + * + * <p><b>Thread Safety:</b> It is safe to call this method from any + * thread without external synchronization. + */ + public synchronized void endUpdate() + { + if (updateLevel == 0) + throw new IllegalStateException(); + + if (--updateLevel > 0) + return; + + compoundEdit.end(); + _postEdit(compoundEdit); + compoundEdit = null; + } +} diff --git a/libjava/classpath/javax/swing/undo/package.html b/libjava/classpath/javax/swing/undo/package.html new file mode 100644 index 000000000..125bd4446 --- /dev/null +++ b/libjava/classpath/javax/swing/undo/package.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in javax.swing.undo package. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - javax.swing.undo</title></head> + +<body> +<p>Provides a mechanism to support undo/redo operations.</p> + +</body> +</html> |