// Copyright 2003-2004, SLAC, Stanford, U.S.A. package org.freehep.swing.undo; import java.awt.Component; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.logging.Logger; import javax.swing.Timer; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; /** * Extension of DoableEdit which allows the Edit to animate a shape before doing * the redoEdit or undoEdit. * * Subclasses of this class should override redoEdit() and undoEdit(). * * @author Mark Donszelmann * @version $Id: AnimatedEdit.java 8584 2006-08-10 23:06:37Z duns $ */ public abstract class AnimatedEdit extends AbstractDoableEdit implements LinkableEdit { private static Logger logger = Logger.getLogger(AnimatedEdit.class.getPackage().getName()); private int frames; private LinkableEdit parent; private LinkableEdit nextEdit, previousEdit; /** * Creates an animated edit with given number of frames. */ public AnimatedEdit(int frames) { this.frames = frames; } 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 getNextEdit() { return nextEdit; } public LinkableEdit getPreviousEdit() { return previousEdit; } public void setNextEdit(LinkableEdit edit) { assert (nextEdit == null) || (edit == null) : "trying to set next while already set"; nextEdit = edit; } public void setPreviousEdit(LinkableEdit edit) { assert(previousEdit == null) || (edit == null) : "trying to set previous while already set"; previousEdit = edit; } /** * Called when the edit is (re)done. Do not override, use redoEdit instead. */ public void redo() throws CannotRedoException { super.redo(); logger.fine(this.toString()); if (frames > 0) { new TimedEdit(true); } else { redoAndNext(); } } private void redoAndNext() throws CannotRedoException { redoEdit(); LinkableEdit current = this; do { LinkableEdit next = current.getNextEdit(); if (next != null) { logger.finer("Found next in list: "+next); next.redo(); return; } current = current.getParent(); } while (current != null); logger.finer("Reached end of list"); } /** * Called when the edit is undone. Do not override, use undoEdit instead. */ public void undo() throws CannotUndoException { super.undo(); logger.fine(this.toString()); if (frames > 0) { new TimedEdit(false); } else { undoAndPrevious(); } } private void undoAndPrevious() throws CannotUndoException { undoEdit(); LinkableEdit current = this; do { LinkableEdit previous = current.getPreviousEdit(); if (previous != null) { logger.finer("Found previous in list: "+previous); previous.undo(); return; } current = current.getParent(); } while (current != null); logger.finer("Reached start of list"); } /** * Returns number of frames. */ public int getFrames() { return frames; } public String toString() { return "AnimatedEdit: ("+frames+")"; } /** * (Re)does the edit. */ protected abstract void redoEdit(); /** * Undoes the edit. */ protected abstract void undoEdit(); /** * Starts the animation for redo or undo. */ protected abstract void startAnimation(boolean redo); /** * Show frameNo of animation. */ protected abstract void showAnimation(int frameNo); /** * Ends the animation. */ protected abstract void endAnimation(); /** * Returns a transformed shape, from the given shape and component. */ public Shape createTransformedShape(Component component, Shape shape) { return shape; } class TimedEdit implements ActionListener { private boolean redo; private Timer timer; private int steps; private long t0; public TimedEdit(boolean redo) { this.redo = redo; t0 = System.currentTimeMillis(); logger.finer("Animation Starts for "+AnimatedEdit.this.getClass()); timer = new Timer(3, this); timer.setInitialDelay(0); timer.start(); AnimatedEdit.this.startAnimation(redo); steps = frames; } public void actionPerformed(ActionEvent event) { if (steps > 0) { steps--; int frameNo = (redo) ? frames-1-steps: steps; AnimatedEdit.this.showAnimation(frameNo); } else { AnimatedEdit.this.endAnimation(); timer.stop(); logger.finer("Animation Ends for "+AnimatedEdit.this.getClass()+ " in "+(System.currentTimeMillis()-t0)+" ms."); if (redo) { redoAndNext(); } else { undoAndPrevious(); } } } } }