// Copyright 2003-2005, SLAC, Stanford, U.S.A.
package org.freehep.swing.undo;
import java.util.logging.Logger;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoableEdit;
/**
* @author Mark Donszelmann
* @version $Id: AnimatedCompoundEdit.java 8584 2006-08-10 23:06:37Z duns $
*/
public class AnimatedCompoundEdit extends CompoundEdit implements DoableEdit, LinkableEdit {
private static Logger logger = Logger.getLogger(AnimatedCompoundEdit.class.getPackage().getName());
/**
* True if this edit has never received <code>end</code>.
*/
boolean inProgress;
boolean hasBeenDone;
boolean alive;
String name;
/**
* The start of the LinkableEdit sub chain
*/
LinkableEdit first, last;
LinkableEdit parent;
LinkableEdit nextEdit, previousEdit;
/**
* Create an AnimatedCompoundEdit that still needs to be "(re)done".
*/
public AnimatedCompoundEdit() {
this(null);
}
public AnimatedCompoundEdit(String name) {
this(name, false);
}
public AnimatedCompoundEdit(boolean done) {
this(null, done);
}
/**
* Create an AnimatedCompoundEdit that can be in either "done" or "undone" state.
*/
public AnimatedCompoundEdit(String name, boolean done) {
super();
inProgress = true;
hasBeenDone = done;
alive = true;
this.name = name;
logger.fine("begin "+((name != null) ? name : "anonymous"));
}
public LinkableEdit getParent() {
return parent;
}
public void setParent(LinkableEdit parent) {
assert(this.parent == null) : "parent can only be set once";
this.parent = parent;
}
public LinkableEdit getFirstEdit() {
return first;
}
public LinkableEdit getLastEdit() {
return last;
}
public LinkableEdit getNextEdit() {
return nextEdit;
}
public void setNextEdit(LinkableEdit edit) {
assert (nextEdit == null) || (edit == null) : "trying to set next while already set";
nextEdit = edit;
}
public LinkableEdit getPreviousEdit() {
return previousEdit;
}
public void setPreviousEdit(LinkableEdit edit) {
assert(previousEdit == null) || (edit == null) : "trying to set previous while already set";
previousEdit = edit;
}
/**
* Sends <code>undo</code> to all contained
* <code>LinkableEdits</code> in the reverse of
* the order in which they were added.
*/
public void undo() throws CannotUndoException {
logger.finer("Started on last sub-edit");
if (!canUndo()) throw new CannotUndoException();
last.undo();
hasBeenDone = false;
}
/**
* Sends <code>redo</code> to all contained
* <code>LinkableEdit</code>s in the order in
* which they were added.
*/
public void redo() throws CannotRedoException {
logger.finer("Started on first sub-edit");
if (!canRedo()) throw new CannotRedoException();
first.redo();
hasBeenDone = true;
}
/**
* Returns the last <code>LinkableEdit</code>, or <code>null</code>.
*/
protected UndoableEdit lastEdit() {
return last;
}
/**
* Sends <code>die</code> to each subedit,
* in the reverse of the order that they were added.
*/
public void die() {
LinkableEdit cursor = last;
while (cursor != null) {
cursor.die();
cursor = cursor.getPreviousEdit();
}
alive = false;
}
/**
* Returns false since only LinkableEdits can be added.
*/
public boolean addEdit(UndoableEdit edit) {
if (edit instanceof LinkableEdit) return addEdit((LinkableEdit)edit);
return false;
}
/**
* If this edit is <code>inProgress</code>,
* accepts <code>anEdit</code> and returns true.
*
* <p>The last edit added to this <code>CompoundEdit</code>
* is given a chance to <code>addEdit(anEdit)</code>.
* If it refuses (returns false), <code>anEdit</code> is
* given a chance to <code>replaceEdit</code> the last edit.
* If <code>anEdit</code> returns false here,
* it is added to <code>edits</code>.
*
* @param edit the edit to be added
* @return true if the edit is <code>inProgress</code>;
* otherwise returns false
*/
public boolean addEdit(LinkableEdit edit) {
logger.fine(edit.toString());
if (!inProgress) return false;
// If this is the first subedit received, just add it.
if (last == null) {
first = edit;
last = edit;
edit.setParent(this);
edit.setNextEdit(null);
edit.setPreviousEdit(null);
return true;
}
// Otherwise, give the last one a chance to absorb the new
// one. If it won't, give the new one a chance to absorb
// the last one.
if (!last.addEdit(edit)) {
if (edit.replaceEdit(last)) {
// remove last edit
last = last.getPreviousEdit();
last.setNextEdit(null);
}
// add new edit as last one
last.setNextEdit(edit);
edit.setParent(this);
edit.setNextEdit(null);
edit.setPreviousEdit(last);
last = edit;
}
return true;
}
/**
* Sets <code>inProgress</code> to false.
*
* @see #canUndo
* @see #canRedo
*/
public void end() {
logger.fine("end");
inProgress = false;
}
/**
* Returns false if <code>isInProgress</code>.
*
* @see #isInProgress
*/
public boolean canUndo() {
return !isInProgress() && alive && hasBeenDone;
}
/**
* Returns false if <code>isInProgress</code>.
*
* @see #isInProgress
*/
public boolean canRedo() {
return !isInProgress() && alive && !hasBeenDone;
}
/**
* Returns true if this edit is in progress--that is, it has not
* received end. This generally means that edits are still being
* added to it.
*
* @see #end
*/
public boolean isInProgress() {
return inProgress;
}
/**
* Returns true if any of the <code>LinkableEdit</code>s
* in <code>edits</code> do.
* Returns false if they all return false.
*/
public boolean isSignificant() {
LinkableEdit cursor = first;
while (cursor != null) {
if (cursor.isSignificant()) {
return true;
}
cursor = cursor.getNextEdit();
}
return false;
}
/**
* Returns name (if set).
*/
public String getName() {
return name;
}
/**
* Returns <code>getPresentationName</code> from the
* last <code>LinkableEdit</code> added. If this edit is empty,
* calls super.
*/
public String getPresentationName() {
if (name != null) return name;
if (last != null) return last.getPresentationName();
return super.getPresentationName();
}
/**
* Returns <code>getUndoPresentationName</code>
* from the last <code>LinkableEdit</code>
* added.
* If edit is empty, calls super.
*/
public String getUndoPresentationName() {
if (name != null) return "Undo "+name;
if (last != null) return last.getUndoPresentationName();
return super.getUndoPresentationName();
}
/**
* Returns <code>getRedoPresentationName</code>
* from the last <code>LinkableEdit</code>
* added.
* If edit is empty, calls super.
*/
public String getRedoPresentationName() {
if (name != null) return "Redo "+name;
if (last != null) return last.getRedoPresentationName();
return super.getRedoPresentationName();
}
/**
* Returns a string that displays and identifies this
* object's properties.
*
* @return a String representation of this object
*/
public String toString() {
if (name != null) return name;
if (last != null) return last.toString();
return super.toString();
}
}