[pkg-java] r13103 - in branches/upstream: . timingframework timingframework/current timingframework/current/org timingframework/current/org/jdesktop timingframework/current/org/jdesktop/animation timingframework/current/org/jdesktop/animation/timing timingframework/current/org/jdesktop/animation/timing/interpolation timingframework/current/org/jdesktop/animation/timing/triggers

Andrew Ross rockclimb-guest at alioth.debian.org
Sat Nov 27 19:08:00 UTC 2010


Author: rockclimb-guest
Date: 2010-11-27 19:07:53 +0000 (Sat, 27 Nov 2010)
New Revision: 13103

Added:
   branches/upstream/timingframework/
   branches/upstream/timingframework/current/
   branches/upstream/timingframework/current/org/
   branches/upstream/timingframework/current/org/jdesktop/
   branches/upstream/timingframework/current/org/jdesktop/animation/
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/Animator.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingEventListener.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingSource.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTarget.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTargetAdapter.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/DiscreteInterpolator.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Evaluator.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Interpolator.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyFrames.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyInterpolators.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyTimes.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyValues.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/LinearInterpolator.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/PropertySetter.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/SplineInterpolator.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/package.html
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/package.html
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/ActionTrigger.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTrigger.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTriggerEvent.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTrigger.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTriggerEvent.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTrigger.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTriggerEvent.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/Trigger.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerEvent.java
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerNotes
   branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/package.html
Log:
[svn-inject] Installing original source of timingframework (1.0)

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/Animator.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/Animator.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/Animator.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,1045 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.jdesktop.animation.timing;
+
+import javax.swing.Timer;
+import java.awt.event.*;
+import java.util.ArrayList;
+import org.jdesktop.animation.timing.interpolation.Interpolator;
+import org.jdesktop.animation.timing.interpolation.LinearInterpolator;
+
+/**
+ * This class controls animations.  Its constructors and various
+ * set methods control the parameters under which animations are run,
+ * and the other methods support starting and stopping the animation.
+ * The parameters of this class use the concepts of a "cycle" (the base
+ * animation) and an "envelope" that controls how the cycle is started,
+ * ended, and repeated.
+ * <p>
+ * Most of the methods here are simle getters/setters for the properties
+ * used by Animator.  Typical animations will simply use one of the 
+ * two constructors (depending on whether you are constructing a repeating
+ * animation), optionally call any of the <code>set*</code> methods to alter
+ * any of the other parameters, and then call start() to run the animation.
+ * For example, this animation will run for 1 second, calling your
+ * {@link TimingTarget} with timing events when the animation is started,
+ * running, and stopped:
+ * <pre>
+ *  Animator animator = new Animator(1000, myTarget);
+ *  animator.start();
+ * </pre>
+ * The following variation will run a half-second animation 4 times, 
+ * reversing direction each time:
+ * <pre>
+ *  Animator animator = new Animator(500, 4, RepeatBehavior.REVERSE, myTarget);
+ *  animator.start();
+ * </pre>
+ * More complex animations can be created through using the properties
+ * in Animator, such as {@link Animator#setAcceleration acceleration} and {@link
+ * Animator#setDeceleration}. More automated animations can be created and run
+ * using the {@link org.jdesktop.animation.timing.triggers triggers}
+ * package to control animations through events and {@link
+ * org.jdesktop.animation.timing.interpolation.PropertySetter} to 
+ * handle animating object properties.
+ */
+public final class Animator {
+
+    private TimingSource timer;    // Currently uses Swing timer.  This could change
+			    // in the future to use a more general mechanism
+			    // (and one of better timing resolution). An 
+                            // important advantage to the Swing timer is that
+                            // it ensures that we receive and send our timing
+                            // events on the Event Dispatch Thread, which makes
+                            // it easier to use the framework for GUI
+                            // animations.
+    private TimingSource swingTimer;
+    private TimingSourceTarget timingSourceTarget;
+    
+    private ArrayList<TimingTarget> targets = new ArrayList<TimingTarget>();    // Animators may have 
+                                                    // multiple targets
+
+    private long startTime;	    // Tracks original Animator start time
+    private long currentStartTime;  // Tracks start time of current cycle
+    private int currentCycle = 0;   // Tracks number of cycles so far
+    private boolean intRepeatCount = true;  // for typical cases
+                                            // of repeated cycles
+    private boolean timeToStop = false;     // This gets triggered during
+                                            // fraction calculation
+    private boolean hasBegun = false;
+    private long pauseBeginTime = 0;        // Used for pause/resume
+    private boolean running = false;        // Used for isRunning()
+    
+    // Private variables to hold the internal "envelope" values that control
+    // how the cycle is started, ended, and repeated.
+    private double repeatCount = 1.0;
+    private int startDelay;
+    private RepeatBehavior repeatBehavior = RepeatBehavior.REVERSE;
+    private EndBehavior endBehavior = EndBehavior.HOLD;
+    
+    // Private variables to hold the internal values of the base
+    // animation (the cycle)
+    private int duration;
+    private int resolution = 20;    
+    private float acceleration = 0;
+    private float deceleration = 0.0f;
+    private float startFraction = 0.0f;
+    private Direction direction = Direction.FORWARD; // Direction of each cycle
+    private Interpolator interpolator = LinearInterpolator.getInstance();
+    
+    /**
+     * EndBehavior determines what happens at the end of the animation.
+     * @see #setEndBehavior
+     */
+    public static enum EndBehavior {
+        /** Timing sequence will maintain its final value at the end */
+	HOLD,
+        /** Timing sequence should reset to the initial value at the end */
+	RESET,
+    };
+
+    /**
+     * Direction is used to set the initial direction in which the
+     * animation starts.
+     * 
+     * @see #setStartDirection
+     */
+    public static enum Direction {
+        /**
+         * cycle proceeds forward
+         */
+	FORWARD,
+        /** cycle proceeds backward */
+	BACKWARD,
+    };
+    
+    /**
+     * RepeatBehavior determines how each successive cycle will flow.
+     * @see #setRepeatBehavior
+     */
+    public static enum RepeatBehavior {
+        /** 
+         * Each repeated cycle proceeds in the same direction as the 
+         * previous one 
+         */
+	LOOP,
+        /** 
+         * Each cycle proceeds in the opposite direction as the 
+         * previous one
+         */
+	REVERSE
+    };
+    
+    /**
+     * Used to specify unending duration or repeatCount
+     * @see #setDuration
+     * @see #setRepeatCount
+     * */
+    public static final int INFINITE = -1;
+
+    private void validateRepeatCount(double repeatCount) {
+        if (repeatCount < 1 && repeatCount != INFINITE) {
+            throw new IllegalArgumentException("repeatCount (" + repeatCount + 
+                    ") cannot be <= 0");
+        }
+    }
+
+    /**
+     * Constructor: this is a utility constructor
+     * for a simple timing sequence that will run for 
+     * <code>duration</code> length of time.  This variant takes no
+     * TimingTarget, and is equivalent to calling {@link #Animator(int, 
+     * TimingTarget)} with a TimingTarget of <code>null</code>.
+     * 
+     * @param duration The length of time that this will run, in milliseconds.
+     */
+
+    public Animator(int duration) {
+        this(duration, null);
+    }
+    
+    /**
+     * Constructor: this is a utility constructor
+     * for a simple timing sequence that will run for 
+     * <code>duration</code> length of time.
+     * 
+     * @param duration The length of time that this will run, in milliseconds.
+     * @param target TimingTarget object that will be called with
+     * all timing events.  Null is acceptable, but no timingEvents will be
+     * sent to any targets without future calls to {@link #addTarget}.
+     */
+    public Animator(int duration, TimingTarget target) {
+        this.duration = duration;
+        addTarget(target);
+
+        /**
+	 * hack workaround for starting the Toolkit thread before any Timer stuff
+	 * javax.swing.Timer uses the Event Dispatch Thread, which is not
+	 * created until the Toolkit thread starts up.  Using the Swing
+	 * Timer before starting this stuff starts up may get unexpected
+	 * results (such as taking a long time before the first timer
+	 * event).
+	 */
+	java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit();	
+
+	// Create internal Timer object
+        swingTimer = new SwingTimingSource();
+        timer = swingTimer;
+    }
+    
+    /**
+     * Constructor that sets the most common properties of a 
+     * repeating animation.
+     * @param duration the length of each animation cycle, in milliseconds.
+     * This value can also be {@link #INFINITE} for animations that have no
+     * end.  Note that fractions sent out with such unending animations will
+     * be undefined since there is no fraction of an infinitely long cycle.
+     * @param repeatCount the number of times the animation cycle will repeat.
+     * This is a positive value, which allows a non-integral number
+     * of repetitions (allowing an animation to stop mid-cycle, for example).
+     * This value can also be {@link #INFINITE}, indicating that the animation
+     * will continue repeating forever, or until manually stopped.
+     * @param repeatBehavior {@link RepeatBehavior} of each successive
+     * cycle.  A value of null is equivalent to RepeatBehavior.REVERSE.
+     * @param target TimingTarget object that will be called with
+     * all timing events.  Null is acceptable, but no timingEvents will be
+     * sent to any targets without future calls to {@link #addTarget}.
+     * @throws IllegalArgumentException if any parameters have invalid
+     * values
+     * @see Animator#INFINITE
+     * @see Direction
+     * @see EndBehavior
+     */
+    public Animator(int duration, double repeatCount, 
+            RepeatBehavior repeatBehavior, TimingTarget target) {
+        this(duration, target);
+	// First, check for bad parameters
+        validateRepeatCount(repeatCount);
+        this.repeatCount = repeatCount;
+        this.repeatBehavior = (repeatBehavior != null) ? 
+            repeatBehavior : RepeatBehavior.REVERSE;
+
+        // Set convenience variable: do we have an integer number of cycles?
+	intRepeatCount = (Math.rint(repeatCount) == repeatCount);
+    }    
+    
+    /**
+     * Returns the initial direction for the animation.
+     * @return direction that the initial animation cycle will be moving
+     */
+    public Direction getStartDirection() {
+        return direction;
+    }
+    
+    /**
+     * Sets the startDirection for the initial animation cycle.  The default 
+     * startDirection is {@link Direction#FORWARD FORWARD}.
+     * 
+     * @param startDirection initial animation cycle direction
+     * @see #isRunning()
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     */
+    public void setStartDirection(Direction startDirection) {
+        throwExceptionIfRunning();
+        this.direction = startDirection;
+    }
+    
+    /**
+     * Returns the interpolator for the animation.
+     * @return interpolator that the initial animation cycle uses
+     */
+    public Interpolator getInterpolator() {
+        return interpolator;
+    }
+    
+    /**
+     * Sets the interpolator for the animation cycle.  The default 
+     * interpolator is {@link LinearInterpolator}.
+     * @param interpolator the interpolation to use each animation cycle
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     */
+    public void setInterpolator(Interpolator interpolator) {
+        throwExceptionIfRunning();
+        this.interpolator = interpolator;
+    }
+    
+    /**
+     * Sets the fraction of the timing cycle that will be spent accelerating
+     * at the beginning. The default acceleration value is 0 (no acceleration).
+     * @param acceleration value from 0 to 1
+     * @throws IllegalArgumentException acceleration value must be between 0 and
+     * 1, inclusive. 
+     * @throws IllegalArgumentException acceleration cannot be greater than
+     * (1 - deceleration)
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     * @see #setDeceleration(float)
+     */
+    public void setAcceleration(float acceleration) {
+        throwExceptionIfRunning();
+        if (acceleration < 0 || acceleration > 1.0f) {
+            throw new IllegalArgumentException("Acceleration value cannot lie" +
+                    " outside [0,1] range");
+        }
+        if (acceleration > (1.0f - deceleration)) {
+            throw new IllegalArgumentException("Acceleration value cannot be" +
+                    " greater than (1 - deceleration)");
+        }
+        this.acceleration = acceleration;
+    }
+    
+    /**
+     * Sets the fraction of the timing cycle that will be spent decelerating
+     * at the end. The default deceleration value is 0 (no deceleration).
+     * @param deceleration value from 0 to 1
+     * @throws IllegalArgumentException deceleration value must be between 0 and
+     * 1, inclusive. 
+     * @throws IllegalArgumentException deceleration cannot be greater than
+     * (1 - acceleration)
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     * @see #setAcceleration(float)
+     */
+    public void setDeceleration(float deceleration) {
+        throwExceptionIfRunning();
+        if (deceleration < 0 || deceleration > 1.0f) {
+            throw new IllegalArgumentException("Deceleration value cannot lie" +
+                    " outside [0,1] range");
+        }
+        if (deceleration > (1.0f - acceleration)) {
+            throw new IllegalArgumentException("Deceleration value cannot be" +
+                    " greater than (1 - acceleration)");
+        }
+        this.deceleration = deceleration;
+    }
+    
+    /**
+     * Returns the current value of acceleration property
+     * @return acceleration value
+     */
+    public float getAcceleration() {
+        return acceleration;
+    }
+    
+    /**
+     * Returns the current value of deceleration property
+     * @return deceleration value
+     */
+    public float getDeceleration() {
+        return deceleration;
+    }
+    
+    /**
+     * Adds a TimingTarget to the list of targets that get notified of each
+     * timingEvent.  This can be done at any time before, during, or after the
+     * animation has started or completed; the new target will begin
+     * having its TimingTarget methods called as soon as it is added.
+     * If <code>target</code> is already on the list of targets in this Animator, it
+     * is not added again (there will be only one instance of any given
+     * target in any Animator's list of targets).
+     * @param target TimingTarget to be added to the list of targets that
+     * get notified by this Animator of all timing events. Target cannot
+     * be null.
+     */
+    public void addTarget(TimingTarget target) {
+        if (target != null) {
+            synchronized (targets) {
+                if (!targets.contains(target)) {
+                    targets.add(target);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Removes the specified TimingTarget from the list of targets that get
+     * notified of each timingEvent.  This can be done at any time before,
+     * during, or after the animation has started or completed; the 
+     * target will cease having its TimingTarget methods called as soon
+     * as it is removed.
+     * @param target TimingTarget to be removed from the list of targets that
+     * get notified by this Animator of all timing events.
+     */
+    public void removeTarget(TimingTarget target) {
+        synchronized (targets) {
+            targets.remove(target);
+        }
+    }
+    
+    /**
+     * Private utility to throw an exception if the animation is running.  This
+     * is used by all of the property-setting methods to ensure that the
+     * properties are not being changed mid-stream.
+     */
+    private void throwExceptionIfRunning() {
+        if (isRunning()) {
+            throw new IllegalStateException("Cannot perform this operation " +
+                    "while Animator is running");
+        }
+    }
+    
+    /**
+     * Returns the current resolution of the animation. This helps 
+     * determine the maximum frame rate at which the animation will run.
+     * @return the resolution, in milliseconds, of the timer
+     */
+    public int getResolution() {
+	return resolution;
+    }
+    
+    /**
+     * Sets the resolution of the animation
+     * @param resolution the amount of time between timing events of the
+     * animation, in milliseconds.  Note that the actual resolution may vary,
+     * according to the resolution of the timer used by the framework as well
+     * as system load and configuration; this value should be seen more as a
+     * minimum resolution than a guaranteed resolution.
+     * @throws IllegalArgumentException resolution must be >= 0
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     */
+    public void setResolution(int resolution) {
+        if (resolution < 0) {
+            throw new IllegalArgumentException("resolution must be >= 0");
+        }
+        throwExceptionIfRunning();
+        this.resolution = resolution;
+        timer.setResolution(resolution);
+    }
+    
+    /**
+     * Returns the duration of the animation.
+     * @return the length of the animation, in milliseconds. A
+     * return value of -1 indicates an {@link #INFINITE} duration.
+     */
+    public int getDuration() {
+	return duration;
+    }
+    
+    /**
+     * Sets the duration for the animation
+     * @param duration the length of the animation, in milliseconds.  This
+     * value can also be {@link #INFINITE}, meaning the animation will run
+     * until manually stopped.
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     * @see #stop()
+     */
+    public void setDuration(int duration) {
+        throwExceptionIfRunning();
+        this.duration = duration;
+    }
+
+    /**
+     * Returns the number of times the animation cycle will repeat.
+     * @return the number of times the animation cycle will repeat.
+     */
+    public double getRepeatCount() {
+	return repeatCount;
+    }
+    
+    /**
+     * Sets the number of times the animation cycle will repeat. The default
+     * value is 1.
+     * @param repeatCount Number of times the animation cycle will repeat.
+     * This value may be >= 1 or {@link #INFINITE} for animations that repeat 
+     * indefinitely.  The value may be fractional if the animation should
+     * stop at some fractional point.
+     * @throws IllegalArgumentException if repeatCount is not >=1 or 
+     * INFINITE.
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     */
+    public void setRepeatCount(double repeatCount) {
+        validateRepeatCount(repeatCount);
+        throwExceptionIfRunning();
+        this.repeatCount = repeatCount;
+    }
+
+    /**
+     * Returns the amount of delay prior to starting the first animation
+     * cycle after the call to {@link #start}.
+     * @return the duration, in milliseconds, between the call
+     * to start the animation and the first animation cycle actually 
+     * starting.
+     * @see #start
+     */
+    public int getStartDelay() {
+	return startDelay;
+    }
+
+    /**
+     * Sets the duration of the initial delay between calling {@link #start}
+     * and the start of the first animation cycle. The default value is 0 (no 
+     * delay).
+     * @param startDelay the duration, in milliseconds, between the call
+     * to start the animation and the first animation cycle actually 
+     * starting. This value must be >= 0.
+     * @throws IllegalArgumentException if startDelay is < 0
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     */
+    public void setStartDelay(int startDelay) {
+        if (startDelay < 0) {
+            throw new IllegalArgumentException("startDelay (" + startDelay + 
+                    ") cannot be < 0");
+        }
+        throwExceptionIfRunning();
+        this.startDelay = startDelay;
+        timer.setStartDelay(startDelay);
+    }
+
+    /**
+     * Returns the {@link RepeatBehavior} of the animation. The default
+     * behavior is REVERSE, meaning that the animation will reverse direction
+     * at the end of each cycle.
+     * @return whether the animation will repeat in the same
+     * direction or will reverse direction each time.
+     */
+    public RepeatBehavior getRepeatBehavior() {
+	return repeatBehavior;
+    }
+    
+    /**
+     * Sets the {@link RepeatBehavior} of the animation.
+     * @param repeatBehavior the behavior for each successive cycle in the
+     * animation.  A null behavior is equivalent to specifying the default:
+     * REVERSE.  The default behaviors is HOLD.
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning()
+     */
+    public void setRepeatBehavior(RepeatBehavior repeatBehavior) {
+        throwExceptionIfRunning();
+        this.repeatBehavior = (repeatBehavior != null) ? 
+            repeatBehavior : RepeatBehavior.REVERSE;
+    }
+
+    /**
+     * Returns the {@link EndBehavior} of the animation, either HOLD to 
+     * retain the final value or RESET to take on the initial value. The 
+     * default behavior is HOLD.
+     * @return the behavior at the end of the animation
+     */
+    public EndBehavior getEndBehavior() {
+	return endBehavior;
+    }
+    
+    /**
+     * Sets the behavior at the end of the animation.
+     * @param endBehavior the behavior at the end of the animation, either
+     * HOLD or RESET.  A null value is equivalent to the default value of
+     * HOLD.
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     * @see #isRunning
+     */
+    public void setEndBehavior(EndBehavior endBehavior) {
+        throwExceptionIfRunning();
+        this.endBehavior = endBehavior;
+    }
+    
+    /**
+     * Returns the fraction that the first cycle will start at.
+     * @return fraction between 0 and 1 at which the first cycle will start.
+     */
+    public float getStartFraction() {
+        return startFraction;
+    }
+    
+    /**
+     * Sets the initial fraction at which the first animation cycle will
+     * begin.  The default value is 0.
+     * @param startFraction
+     * @see #isRunning()
+     * @throws IllegalArgumentException if startFraction is less than 0
+     * or greater than 1
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended
+     */
+    public void setStartFraction(float startFraction) {
+        if (startFraction < 0 || startFraction > 1.0f) {
+            throw new IllegalArgumentException("initialFraction must be " +
+                    "between 0 and 1");
+        }
+        throwExceptionIfRunning();
+        this.startFraction = startFraction;
+    }
+    
+    /**
+     * Starts the animation
+     * @throws IllegalStateException if animation is already running; this
+     * command may only be run prior to starting the animation or 
+     * after the animation has ended
+     */
+    public void start() {
+        throwExceptionIfRunning();
+        hasBegun = false;
+        running = true;
+	// Initialize start time variables to current time
+	startTime = (System.nanoTime() / 1000000) + getStartDelay();
+        if (duration != INFINITE &&
+                ((direction == Direction.FORWARD && startFraction > 0.0f) ||
+                 (direction == Direction.BACKWARD && startFraction < 1.0f))) {
+            float offsetFraction = (direction == Direction.FORWARD) ?
+                startFraction : (1.0f - startFraction);
+            long startDelta = (long)(duration * offsetFraction);
+            startTime -= startDelta;
+        }
+	currentStartTime = startTime;
+	timer.start();
+    }
+
+    /**
+     * Returns whether this Animator object is currently running
+     */
+    public boolean isRunning() {
+	return running;
+    }
+
+    /**
+     * This method is optional; animations will always stop on their own
+     * if Animator is provided with appropriate values for
+     * duration and repeatCount in the constructor.  But if the application 
+     * wants to stop the timer mid-stream, this is the method to call.
+     * This call will result in calls to the <code>end()</code> method
+     * of all TimingTargets of this Animator.
+     * @see #cancel()
+     */
+    public void stop() {
+	timer.stop();
+        end();
+        timeToStop = false;
+        running = false;
+        pauseBeginTime = 0;
+    }
+
+    /**
+     * This method is like the {@link #stop} method, only this one will
+     * not result in a calls to the <code>end()</code> method in all 
+     * TimingTargets of this Animation; it simply cancels the Animator
+     * immediately.
+     * @see #stop()
+     */
+    public void cancel() {
+        timer.stop();
+        timeToStop = false;
+        running = false;
+        pauseBeginTime = 0;
+    }
+    
+    /**
+     * This method pauses a running animation.  No further events are sent to
+     * TimingTargets. A paused animation may be d again by calling the
+     * {@link #resume} method.  Pausing a non-running animation has no effect.
+     * 
+     * @see #resume()
+     * @see #isRunning()
+     */
+    public void pause() {
+        if (isRunning()) {
+            pauseBeginTime = System.nanoTime();
+            running = false;
+            timer.stop();
+        }
+    }
+    
+    /**
+     * This method resumes a paused animation.  Resuming an animation that
+     * is not paused has no effect.
+     *
+     * @see #pause()
+     */
+    public void resume() {
+        if (pauseBeginTime > 0) {
+            long pauseDelta = (System.nanoTime() - pauseBeginTime) / 1000000;
+            startTime += pauseDelta;
+            currentStartTime += pauseDelta;
+            timer.start();
+            pauseBeginTime = 0;
+            running = true;
+        }
+    }
+    
+    //
+    // TimingTarget implementations
+    // Note that Animator does not actually implement TimingTarget directly;
+    // it does not want to make public methods of these events.  But it uses
+    // the same methods internally to propagate the events to all of the
+    // Animator's targets.
+    //
+
+    /**
+     * Internal timingEvent method that sends out the event to all targets
+     */
+    private void timingEvent(float fraction) {
+        synchronized (targets) {
+            for (int i = 0; i < targets.size(); ++i) {
+                TimingTarget target = targets.get(i);
+                target.timingEvent(fraction);
+            }
+        }
+        if (timeToStop) {
+            stop();
+        }
+    }
+    
+    /**
+     * Internal begin event that sends out the event to all targets
+     */
+    private void begin() {
+        synchronized (targets) {
+            for (int i = 0; i < targets.size(); ++i) {
+                TimingTarget target = targets.get(i);
+                target.begin();
+            }
+        }
+    }
+    
+    /**
+     * Internal end event that sends out the event to all targets
+     */
+    private void end() {
+        synchronized (targets) {
+            for (int i = 0; i < targets.size(); ++i) {
+                TimingTarget target = targets.get(i);
+                target.end();
+            }
+        }
+    }
+    
+    /**
+     * Internal repeat event that sends out the event to all targets
+     */
+    private void repeat() {
+        synchronized (targets) {
+            for (int i = 0; i < targets.size(); ++i) {
+                TimingTarget target = targets.get(i);
+                target.repeat();
+            }
+        }
+    }
+    
+    /**
+     * This method calculates a new fraction value based on the
+     * acceleration and deceleration settings of Animator.  It then
+     * passes this value through the interpolator (by default, 
+     * a LinearInterpolator) before returning it to the caller (who
+     * will then call the timingEvent() methods in the TimingTargets
+     * with this fraction).
+     */
+    private float timingEventPreprocessor(float fraction) {
+        // First, take care of acceleration/deceleration factors
+        if (acceleration != 0 || deceleration != 0.0f) {
+            // See the SMIL 2.0 specification for details on this
+            // calculation
+            float oldFraction = fraction;
+            float runRate = 1.0f / (1.0f - acceleration/2.0f - 
+                    deceleration/2.0f);
+            if (fraction < acceleration) {
+                float averageRunRate = runRate * (fraction / acceleration) / 2;
+                fraction *= averageRunRate;
+            } else if (fraction > (1.0f - deceleration)) {
+                // time spent in deceleration portion
+                float tdec = fraction - (1.0f - deceleration);
+                // proportion of tdec to total deceleration time
+                float pdec  = tdec / deceleration;
+                fraction = runRate * (1.0f - ( acceleration / 2) -
+                        deceleration + tdec * (2 - pdec) / 2);
+            } else {
+                fraction = runRate * (fraction - (acceleration / 2));
+            }
+            // clamp fraction to [0,1] since above calculations may
+            // cause rounding errors
+            if (fraction < 0) {
+                fraction = 0;
+            } else if (fraction > 1.0f) {
+                fraction = 1.0f;
+            }
+        }
+        // run the result through the current interpolator
+        return interpolator.interpolate(fraction);
+    }
+    
+    /**
+     * Returns the total elapsed time for the current animation.
+     * @param currentTime value of current time to use in calculating
+     * elapsed time.
+     * @return the total time elapsed between the time
+     * the Animator started and the supplied currentTime.
+     */
+    public long getTotalElapsedTime(long currentTime) {
+        return (currentTime - startTime);
+    }
+
+    /**
+     * Returns the total elapsed time for the current animation.  Calculates
+     * current time.
+     * @return the total time elapsed between the time
+     * the Animator started and the current time.
+     */
+    public long getTotalElapsedTime() {
+        long currentTime = System.nanoTime() / 1000000;
+        return getTotalElapsedTime(currentTime);
+    }
+    
+    /**
+     * Returns the elapsed time for the current animation cycle.
+     * @param currentTime value of current time to use in calculating
+     * elapsed time.
+     * @return the time elapsed between the time
+     * this cycle started and the supplied currentTime.
+     */
+    public long getCycleElapsedTime(long currentTime) {
+        return (currentTime - currentStartTime);
+    }
+
+    /**
+     * Returns the elapsed time for the current animation cycle. Calculates
+     * current time.
+     * @return the time elapsed between the time
+     * this cycle started and the current time.
+     */
+    public long getCycleElapsedTime() {
+        long currentTime = System.nanoTime() / 1000000;
+        return getCycleElapsedTime(currentTime);
+    }
+    
+    /**
+     * This method calculates and returns the fraction elapsed of the current
+     * cycle based on the current time
+     * @return fraction elapsed of the current animation cycle
+     */
+    public float getTimingFraction() {
+        long currentTime = System.nanoTime() / 1000000;
+        long cycleElapsedTime = getCycleElapsedTime(currentTime);
+        long totalElapsedTime = getTotalElapsedTime(currentTime);
+        double currentCycle = (double)totalElapsedTime / duration;
+        float fraction;
+
+        if (!hasBegun) {
+            // Call begin() first time after calling start()
+            begin();
+            hasBegun = true;
+        }
+        if ((duration != INFINITE) && (repeatCount != INFINITE) && 
+                (currentCycle >= repeatCount)) {
+            // Envelope done: stop based on end behavior
+            switch (endBehavior) {
+            case HOLD:
+                // Make sure we send a final end value
+                if (intRepeatCount) {
+                    // If supposed to run integer number of cycles, hold
+                    // on integer boundary
+                    if (direction == Direction.BACKWARD) {
+                        // If we were traveling backward, hold on 0
+                        fraction = 0.0f;
+                    } else {
+                        fraction = 1.0f;
+                    }
+                } else {
+                    // hold on final value instead
+                    fraction = Math.min(1.0f, 
+                        ((float)cycleElapsedTime / duration));
+                }
+                break;
+            case RESET:
+                // RESET requires setting the final value to the start value
+                fraction = 0.0f;
+                break;
+            default:
+                fraction = 0.0f;
+                // should not reach here
+                break;
+            }
+            timeToStop = true;
+        } else if ((duration != INFINITE) && (cycleElapsedTime > duration)) {
+            // Cycle end: Time to stop or change the behavior of the timer
+            long actualCycleTime = cycleElapsedTime % duration;
+            fraction = (float)actualCycleTime / duration;
+            // Set new start time for this cycle
+            currentStartTime = currentTime - actualCycleTime;
+
+            if (repeatBehavior == RepeatBehavior.REVERSE) {
+                boolean oddCycles = 
+                        ((int)(cycleElapsedTime / duration) % 2)
+                        > 0;
+                if (oddCycles) {
+                    // reverse the direction
+                    direction = (direction == Direction.FORWARD) ? 
+                            Direction.BACKWARD :
+                            Direction.FORWARD;
+                }
+                if (direction == Direction.BACKWARD) {
+                    fraction = 1.0f - fraction;
+                }
+            }
+            repeat();
+        } else {
+            // mid-stream: calculate fraction of animation between
+            // start and end times and send fraction to target
+            fraction = 0.0f;
+            if (duration != INFINITE) {
+                // Only limited duration animations need a fraction
+                fraction = (float)cycleElapsedTime / duration;
+                if (direction == Direction.BACKWARD) {
+                    // If this is a reversing cycle, want to know inverse
+                    // fraction; how much from start to finish, not 
+                    // finish to start
+                    fraction = (1.0f - fraction);
+                }
+                // Clamp fraction in case timing mechanism caused out of 
+                // bounds value
+                fraction = Math.min(fraction, 1.0f);
+                fraction = Math.max(fraction, 0.0f);
+            }
+        }
+        return timingEventPreprocessor(fraction);
+    }
+    
+    /**
+     * Sets a new TimingSource that will supply the timing 
+     * events to this Animator. Animator uses an internal
+     * TimingSource by default and most developers will probably not
+     * need to change this default behavior. But for those wishing to
+     * supply their own timer, this method can be called to
+     * tell Animator to use a different TimingSource instead. Setting a
+     * new TimingSource implicitly removes this Animator as a listener
+     * to any previously-set TimingSource object.
+     * 
+     * @param timer the object that will provide the
+     * timing events to Animator. A value of <code>null</code> is
+     * equivalent to telling Animator to use its default internal
+     * TimingSource object.
+     * @throws IllegalStateException if animation is already running; this
+     * parameter may only be changed prior to starting the animation or 
+     * after the animation has ended.
+     */
+    public synchronized void setTimer(TimingSource timer) {
+        throwExceptionIfRunning();
+        if (this.timer != swingTimer) {
+            // Remove this Animator from any previously-set external timer
+            this.timer.removeEventListener(timingSourceTarget);
+        }
+        if (timer == null) {
+            this.timer = swingTimer;
+        } else {
+            this.timer = timer;
+            if (timingSourceTarget == null) {
+                timingSourceTarget = new TimingSourceTarget();
+            }
+            timer.addEventListener(timingSourceTarget);
+        }
+        // sync this new timer with existing timer properties
+        this.timer.setResolution(resolution);
+        this.timer.setStartDelay(startDelay);
+    }
+        
+    /**
+     * This package-private class will be called by TimingSource.timingEvent()
+     * when a timer sends in timing events to this Animator.
+     */
+    class TimingSourceTarget implements TimingEventListener {
+        public void timingSourceEvent(TimingSource timingSource) {
+            // Make sure that we are being called by the current timer
+            // and that the animation is actually running
+            if ((timer == timingSource) && running) {
+                timingEvent(getTimingFraction());
+            }
+        }
+    }
+    
+    /**
+     * Implementation of internal timer, which uses the Swing Timer class.
+     * Note that we do not bother going through the TimingSource.timingEvent()
+     * class with our timing events; they go through the TimerTarget
+     * ActionListener implementation and then directly to timingEvent(fraction).
+     */
+    private class SwingTimingSource extends TimingSource {
+        Timer timer; // Swing timer
+        
+        public SwingTimingSource() {
+            timer = new Timer(resolution, new TimerTarget());
+            timer.setInitialDelay(0);
+        }
+        
+        public void start() {
+            timer.start();
+        }
+        
+        public void stop() {
+            timer.stop();
+        }
+        
+        public void setResolution(int resolution) {
+            timer.setDelay(resolution);
+        }
+        
+        public void setStartDelay(int delay) {
+            timer.setInitialDelay(delay);
+        }
+    }
+    
+    /**
+     * Internal implementation detail: we happen to use javax.swing.Timer
+     * currently, which sends its timing events to an ActionListener.
+     * This internal private class is our ActionListener that traps
+     * these calls and forwards them to the Animator.timingEvent(fraction)
+     * method.
+     */
+    private class TimerTarget implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+            timingEvent(getTimingFraction());
+	}
+    }
+
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingEventListener.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingEventListener.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingEventListener.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2007, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing;
+
+/**
+ * This interface is implemented by any object wishing to receive events from a
+ * {@link TimingSource} object. The TimingEventListener would be added as a 
+ * listener to the TimingSource object via the {@link 
+ * TimingSource#addEventListener(TimingEventListener)} method.
+ * <p>
+ * This functionality is handled automatically inside of {@link Animator}. To
+ * use a non-default TimingSource object for Animator, simply call 
+ * {@link Animator#setTimer(TimingSource)} and the appropriate listeners
+ * will be set up internally.
+ *
+ * @author Chet
+ */
+public interface TimingEventListener {
+    
+    /**
+     * This method is called by the {@link TimingSource} object while the
+     * timer is running.
+     *
+     * @param timingSource the object that generates the timing events.
+     */
+    public void timingSourceEvent(TimingSource timingSource);
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingSource.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingSource.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingSource.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2007, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides a generic wrapper for arbitrary
+ * Timers that may be used with the Timing Framework.
+ * Animator creates its own internal TimingSource by default,
+ * but an Animator can be directed to use a different 
+ * TimingSource by calling {@link Animator#setTimer(TimingSource)}.
+ *
+ * The implementation details of any specific timer may
+ * vary widely, but any timer should be able to expose
+ * the basic capabilities used in this interface. Animator
+ * depends on these capabilities for starting, stopping,
+ * and running any TimingSource.
+ *
+ * The usage of an external TimingSource object for sending in timing
+ * events to an Animator is to implement this interface appropriately, 
+ * pass in that object to {@link Animator#setTimer(TimingSource)},
+ * which adds the Animator as a listener to the TimingSource object,
+ * and then send in any later timing events from the object to the
+ * protected method {@link #timingEvent()}, which will send these timing
+ * events to all listeners.
+ * 
+ * @author Chet
+ */
+public abstract class TimingSource {
+
+    // listeners that will receive timing events
+    private ArrayList<TimingEventListener> listeners = 
+            new ArrayList<TimingEventListener>();
+    
+    /**
+     * Starts the TimingSource
+     */
+    public abstract void start();
+    
+    /**
+     * Stops the TimingSource
+     */
+    public abstract void stop();
+        
+    /**
+     * Sets the delay between callback events. This 
+     * will be called by Animator if its
+     * {@link Animator#setResolution(int) setResolution(int)}
+     * method is called. Note that the actual resolution may vary,
+     * according to the resolution of the timer used by the framework as well
+     * as system load and configuration; this value should be seen more as a
+     * minimum resolution than a guaranteed resolution.
+     * @param resolution delay, in milliseconds, between 
+     * each timing event callback.
+     * @throws IllegalArgumentException resolution must be >= 0
+     * @see Animator#setResolution(int)
+     */
+    public abstract void setResolution(int resolution);
+    
+    /**
+     * Sets delay which should be observed by the 
+     * TimingSource after a call to {@link #start()}. Some timers may not be
+     * able to adhere to specific resolution requests
+     * @param delay delay, in milliseconds, to pause before
+     * starting timing events.
+     * @throws IllegalArgumentException resolution must be >= 0
+     * @see Animator#setStartDelay(int)
+     */
+    public abstract void setStartDelay(int delay);
+    
+    /**
+     * Adds a TimingEventListener to the set of listeners that
+     * receive timing events from this TimingSource.
+     * @param listener the listener to be added.
+     */
+    public final void addEventListener(TimingEventListener listener) {
+        synchronized(listeners) {
+            if (!listeners.contains(listener)) {
+                listeners.add(listener);
+            }
+        }
+    }
+    
+    /**
+     * Removes a TimingEventListener from the set of listeners that
+     * receive timing events from this TimingSource.
+     * @param listener the listener to be removed.
+     */
+    public final void removeEventListener(TimingEventListener listener) {
+        synchronized(listeners) {
+            listeners.remove(listener);
+        }
+    }
+    
+
+    /**
+     * Subclasses call this method to post timing events to this
+     * object's {@link TimingEventListener} objects.
+     */
+     protected final void timingEvent() {
+         synchronized(listeners) {
+             for (TimingEventListener listener : listeners) {
+                 listener.timingSourceEvent(this);
+             }
+         }
+    }
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTarget.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTarget.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTarget.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing;
+
+/**
+ * This interface provides the methods which
+ * are called by Animator during the course of a timing
+ * sequence.  Applications
+ * that wish to receive timing events will either create a subclass
+ * of TimingTargetAdapter and override or they can create or use
+ * an implementation of TimingTarget. A TimingTarget can be passed
+ * into the constructor of Animator or set later with the 
+ * {@link Animator#addTarget(TimingTarget)}
+ * method.  Any Animator may have multiple TimingTargets.
+ */
+public interface TimingTarget {
+    
+    /**
+     * This method will receive all of the timing events from an Animator
+     * during an animation.  The fraction is the percent elapsed (0 to 1)
+     * of the current animation cycle.
+     * @param fraction the fraction of completion between the start and
+     * end of the current cycle.  Note that on reversing cycles
+     * ({@link Animator.Direction#BACKWARD}) the fraction decreases
+     * from 1.0 to 0 on backwards-running cycles.  Note also that animations
+     * with a duration of {@link Animator#INFINITE INFINITE} will call
+     * timingEvent with an undefined value for fraction, since there is
+     * no fraction that makes sense if the animation has no defined length.
+     * @see Animator.Direction
+     */
+    public void timingEvent(float fraction);
+
+    /**
+     * Called when the Animator's animation begins.  This provides a chance
+     * for targets to perform any setup required at animation start time.
+     */
+    public void begin();
+    
+    /**
+     * Called when the Animator's animation ends
+     */
+    public void end();
+    
+    /**
+     * Called when the Animator repeats the animation cycle
+     */
+    public void repeat();
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTargetAdapter.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTargetAdapter.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/TimingTargetAdapter.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing;
+
+/**
+ * Implements the {@link TimingTarget} interface, providing stubs for all
+ * TimingTarget methods.  Subclasses may extend this adapter rather than
+ * implementing the TimingTarget interface if they only care about a 
+ * subset of the events that TimingTarget provides.  For example, 
+ * sequencing animations may only require monitoring the 
+ * {@link TimingTarget#end} method, so subclasses of this adapter
+ * may ignore the other methods such as timingEvent.
+ *
+ * @author Chet
+ */
+public class TimingTargetAdapter implements TimingTarget {
+
+    /**
+     * This method will receive all of the timing events from an Animator
+     * during an animation.  The fraction is the percent elapsed (0 to 1)
+     * of the current animation cycle.
+     * @param fraction the fraction of completion between the start and
+     * end of the current cycle.  Note that on reversing cycles
+     * ({@link Animator.Direction#BACKWARD}) the fraction decreases
+     * from 1.0 to 0 on backwards-running cycles.  Note also that animations
+     * with a duration of {@link Animator#INFINITE INFINITE} will call
+     * timingEvent with an undefined value for fraction, since there is
+     * no fraction that makes sense if the animation has no defined length.
+     * @see Animator.Direction
+     */
+    public void timingEvent(float fraction) {}
+
+    /**
+     * Called when the Animator's animation begins.  This provides a chance
+     * for targets to perform any setup required at animation start time.
+     */
+    public void begin() {}
+    
+    /**
+     * Called when the Animator's animation ends
+     */
+    public void end() {}
+    
+    /**
+     * Called when the Animator repeats the animation cycle
+     */
+    public void repeat() {}
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/DiscreteInterpolator.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/DiscreteInterpolator.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/DiscreteInterpolator.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+/**
+ * This class implements the Interpolator interface.  It should
+ * be used in cases where a "discrete" animation is desired.  A
+ * discrete animation is defined to be one where the values during
+ * an animation do not change smoothly between the boundary values,
+ * but suddenly, at the boundary points.  For example, a discrete animation
+ * with KeyFrames where the KeyTimes are {0, .5, 1.0} and the KeyValues
+ * are (0, 1, 2} would, during the animation, retain the value of 0 until
+ * half-way through the animation and 1 through the rest of the animation.
+ * <p>
+ * Because there is no variation to this class, it is a singleton and
+ * is referenced by using the {@link #getInstance} static method.
+ *
+ * @author Chet
+ */
+public final class DiscreteInterpolator implements Interpolator {
+    
+    private static DiscreteInterpolator instance = null;
+    
+    private DiscreteInterpolator() {}
+    
+    /**
+     * Returns the single DiscreteInterpolator object
+     */
+    public static DiscreteInterpolator getInstance() {
+        if (instance == null) {
+            instance = new DiscreteInterpolator();
+        }
+        return instance;
+    }
+    
+    /**
+     * This method always returns 0 for inputs less than 1, 
+     * which will force users of this
+     * interpolation to assign a value equal to the value at the beginning
+     * of this timing interval, which is the desired behavior for discrete
+     * animations.  An input of 1 will return 1, since this means the
+     * end of the current interval (and start to the next interval).
+     * @param fraction a value between 0 and 1, representing the elapsed
+     * fraction of a time interval (either an entire animation cycle or an 
+     * interval between two KeyTimes, depending on where this Interpolator has
+     * been set)
+     * @return number representing the start of the current interval, usually
+     * 0, but if <code>fracton == 0</code>, returns 1.
+     */
+    public float interpolate(float fraction) {
+        if (fraction < 1.0f) {
+            return 0;
+        }
+        return 1.0f;
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Evaluator.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Evaluator.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Evaluator.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,399 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.awt.Color;
+import java.awt.geom.Arc2D;
+import java.awt.geom.CubicCurve2D;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.QuadCurve2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is used by KeyValues to calculate intermediate values for
+ * specific types.
+ * This class has built-in support for the following data types:
+ * <ul>
+ * <li> java.lang.Byte
+ * <li> java.lang.Short
+ * <li> java.lang.Integer
+ * <li> java.lang.Long
+ * <li> java.lang.Float
+ * <li> java.lang.Double
+ * <li> java.awt.Color
+ * <li> java.awt.geom.Point2D
+ * <li> java.awt.geom.Line2D
+ * <li> java.awt.geom.Dimension2D
+ * <li> java.awt.geom.Rectangle2D
+ * <li> java.awt.geom.RoundRectangle2D
+ * <li> java.awt.geom.Ellipse2D
+ * <li> java.awt.geom.Arc2D
+ * <li> java.awt.geom.QuadCurve2D
+ * <li> java.awt.geom.CubicCurve2D
+ * </ul>
+ *
+ * @author Chet
+ */
+public abstract class Evaluator<T> {
+
+    /**
+     * HashMap that holds all registered evaluators
+     */
+    private static final Map<Class<?>, Class<? extends Evaluator>> 
+            impls = new HashMap<Class<?>, 
+            Class<? extends Evaluator>>();
+    
+    /**
+     * Static registration of pre-defined evaluators
+     */
+    static {
+        impls.put(Byte.class,             EvaluatorByte.class);
+        impls.put(Short.class,            EvaluatorShort.class);
+        impls.put(Integer.class,          EvaluatorInteger.class);
+        impls.put(Long.class,             EvaluatorLong.class);
+        impls.put(Float.class,            EvaluatorFloat.class);
+        impls.put(Double.class,           EvaluatorDouble.class);
+        impls.put(Color.class,            EvaluatorColor.class);
+        impls.put(Point2D.class,          EvaluatorPoint2D.class);
+        impls.put(Line2D.class,           EvaluatorLine2D.class);
+        impls.put(Dimension2D.class,      EvaluatorDimension2D.class);
+        impls.put(Rectangle2D.class,      EvaluatorRectangle2D.class);
+        impls.put(RoundRectangle2D.class, EvaluatorRoundRectangle2D.class);
+        impls.put(Ellipse2D.class,        EvaluatorEllipse2D.class);
+        impls.put(Arc2D.class,            EvaluatorArc2D.class);
+        impls.put(QuadCurve2D.class,      EvaluatorQuadCurve2D.class);
+        impls.put(CubicCurve2D.class,     EvaluatorCubicCurve2D.class);
+    }
+
+    private static void register(Class<?> type,
+                                Class<? extends Evaluator> impl)
+    {
+        impls.put(type, impl);
+    }
+
+    private static void deregister(Class<?> type) {
+        impls.remove(type);
+    }
+    
+    static <T> Evaluator<T> create(Class<?> type) {
+        Class<? extends Evaluator> interpClass = null;
+        for (Class<?> klass : impls.keySet()) {
+            if (klass.isAssignableFrom(type)) {
+                interpClass = impls.get(klass);
+                break;
+            }
+        }
+        if (interpClass == null) {
+            throw new IllegalArgumentException("No Evaluator" +
+                    " can be found for type " + type + "; consider using" +
+                    " different types for your values or supplying a custom" +
+                    " Evaluator");
+        }
+        try {
+            Constructor<? extends Evaluator> ctor =
+                interpClass.getConstructor();
+            return (Evaluator<T>)ctor.newInstance();
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Problem constructing " +
+                    "appropriate Evaluator for type " + type +
+                    ":", e);
+        }
+    }
+
+    /**
+     * Abstract method to evaluate between two boundary values.  Built-in
+     * implementations all use linear parametric evaluation:
+     * <pre>
+     *      v = v0 + (v1 - v0) * fraction
+     * </pre>
+     * Extenders of Evaluator will need to override this method
+     * and do something similar for their own types.  Note that this
+     * mechanism may be used to create non-linear interpolators for
+     * specific value types, although it may besimpler to just use 
+     * the linear/parametric interpolation
+     * technique here and perform non-linear interpolation through 
+     * custom Interpolators rather than perform custom calculations in
+     * this method; the point of this class is to allow calculations with
+     * new/unknown types, not to provide another mechanism for non-linear
+     * interpolation.
+     */
+    public abstract T evaluate(T v0, T v1, float fraction);
+}
+
+class EvaluatorByte extends Evaluator<Byte> {
+    public EvaluatorByte() {}
+    public Byte evaluate(Byte v0, Byte v1,
+                            float fraction)
+    {
+        return (byte)(v0 + (byte)((v1 - v0) * fraction));
+    }    
+}
+
+class EvaluatorShort extends Evaluator<Short> {
+    public EvaluatorShort() {}
+    public Short evaluate(Short v0, Short v1,
+                             float fraction)
+    {
+        return (short)(v0 + (short)((v1 - v0) * fraction));
+    }    
+}
+
+class EvaluatorInteger extends Evaluator<Integer> {
+    public EvaluatorInteger() {}
+    public Integer evaluate(Integer v0, Integer v1,
+                               float fraction)
+    {
+        return v0 + (int)((v1 - v0) * fraction);
+    }    
+}
+
+class EvaluatorLong extends Evaluator<Long> {
+    public EvaluatorLong() {}
+    public Long evaluate(Long v0, Long v1,
+                            float fraction)
+    {
+        return v0 + (long)((v1 - v0) * fraction);
+    }    
+}
+
+class EvaluatorFloat extends Evaluator<Float> {
+    public EvaluatorFloat() {}
+    public Float evaluate(Float v0, Float v1,
+                             float fraction)
+    {
+        return v0 + ((v1 - v0) * fraction);
+    }    
+}
+
+class EvaluatorDouble extends Evaluator<Double> {
+    public EvaluatorDouble() {}
+    public Double evaluate(Double v0, Double v1,
+                              float fraction)
+    {
+        return v0 + ((v1 - v0) * fraction);
+    }    
+}
+
+class EvaluatorColor extends Evaluator<Color> {
+    public EvaluatorColor() {}
+    public Color evaluate(Color v0, Color v1,
+                             float fraction)
+    {
+        int r = v0.getRed() +
+            (int)((v1.getRed() - v0.getRed()) * fraction + 0.5f);
+        int g = v0.getGreen() +
+            (int)((v1.getGreen() - v0.getGreen()) * fraction + 0.5f);
+        int b = v0.getBlue() +
+            (int)((v1.getBlue() - v0.getBlue()) * fraction + 0.5f);
+        int a = v0.getAlpha() +
+            (int)((v1.getAlpha() - v0.getAlpha()) * fraction + 0.5f);
+        Color value = new Color(r, g, b, a);
+        return value;
+    }    
+}
+
+class EvaluatorPoint2D extends Evaluator<Point2D> {
+    // REMIND: apply this technique to other classes...
+    private Point2D value;
+    public EvaluatorPoint2D() {}
+    public Point2D evaluate(Point2D v0, Point2D v1,
+                               float fraction)
+    {
+        if (value == null) {
+            // TODO: Note that future calls to this Evaluator may
+            // use a different subclass of Point2D, so the precision of the
+            // result may vary because we are caching a clone of 
+            // the first instance we received
+            value = (Point2D)v0.clone();
+        }
+        double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
+        double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
+        value.setLocation(x, y);
+        return value;
+    }
+}
+
+class EvaluatorLine2D extends Evaluator<Line2D> {
+    public EvaluatorLine2D() {}
+    public Line2D evaluate(Line2D v0, Line2D v1,
+                              float fraction)
+    {
+        double x1 = v0.getX1() + ((v1.getX1() - v0.getX1()) * fraction);
+        double y1 = v0.getY1() + ((v1.getY1() - v0.getY1()) * fraction);
+        double x2 = v0.getX2() + ((v1.getX2() - v0.getX2()) * fraction);
+        double y2 = v0.getY2() + ((v1.getY2() - v0.getY2()) * fraction);
+        Line2D value = (Line2D)v0.clone();
+        value.setLine(x1, y1, x2, y2);
+        return value;
+    }
+}
+
+class EvaluatorDimension2D extends Evaluator<Dimension2D> {
+    public EvaluatorDimension2D() {}
+    public Dimension2D evaluate(Dimension2D v0, Dimension2D v1,
+                                   float fraction)
+    {
+        double w = v0.getWidth() +
+            ((v1.getWidth() - v0.getWidth()) * fraction);
+        double h = v0.getHeight() +
+            ((v1.getHeight() - v0.getHeight()) * fraction);
+        Dimension2D value = (Dimension2D)v0.clone();
+        value.setSize(w, h);
+        return value;
+    }
+}
+
+class EvaluatorRectangle2D extends Evaluator<Rectangle2D> {
+    public EvaluatorRectangle2D() {}
+    public Rectangle2D evaluate(Rectangle2D v0, Rectangle2D v1,
+                                   float fraction)
+    {
+        double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
+        double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
+        double w = v0.getWidth() +
+            ((v1.getWidth() - v0.getWidth()) * fraction);
+        double h = v0.getHeight() +
+            ((v1.getHeight() - v0.getHeight()) * fraction);
+        Rectangle2D value = (Rectangle2D)v0.clone();
+        value.setRect(x, y, w, h);
+        return value;
+    }
+}
+
+class EvaluatorRoundRectangle2D extends Evaluator<RoundRectangle2D> {
+    public EvaluatorRoundRectangle2D() {}
+    public RoundRectangle2D evaluate(RoundRectangle2D v0,
+                                        RoundRectangle2D v1,
+                                        float fraction)
+    {
+        double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
+        double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
+        double w = v0.getWidth() +
+            ((v1.getWidth() - v0.getWidth()) * fraction);
+        double h = v0.getHeight() +
+            ((v1.getHeight() - v0.getHeight()) * fraction);
+        double arcw = v0.getArcWidth() +
+            ((v1.getArcWidth() - v0.getArcWidth()) * fraction);
+        double arch = v0.getArcHeight() +
+            ((v1.getArcHeight() - v0.getArcHeight()) * fraction);
+        RoundRectangle2D value = (RoundRectangle2D)v0.clone();
+        value.setRoundRect(x, y, w, h, arcw, arch);
+        return value;
+    }
+}
+
+class EvaluatorEllipse2D extends Evaluator<Ellipse2D> {
+    public EvaluatorEllipse2D() {}
+    public Ellipse2D evaluate(Ellipse2D v0, Ellipse2D v1,
+                                 float fraction)
+    {
+        double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
+        double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
+        double w = v0.getWidth() +
+            ((v1.getWidth() - v0.getWidth()) * fraction);
+        double h = v0.getHeight() +
+            ((v1.getHeight() - v0.getHeight()) * fraction);
+        Ellipse2D value = (Ellipse2D)v0.clone();
+        value.setFrame(x, y, w, h);
+        return value;
+    }
+}
+
+class EvaluatorArc2D extends Evaluator<Arc2D> {
+    public EvaluatorArc2D() {}
+    public Arc2D evaluate(Arc2D v0, Arc2D v1,
+                             float fraction)
+    {
+        double x = v0.getX() + ((v1.getX() - v0.getX()) * fraction);
+        double y = v0.getY() + ((v1.getY() - v0.getY()) * fraction);
+        double w = v0.getWidth() +
+            ((v1.getWidth() - v0.getWidth()) * fraction);
+        double h = v0.getHeight() +
+            ((v1.getHeight() - v0.getHeight()) * fraction);
+        double start = v0.getAngleStart() +
+            ((v1.getAngleStart() - v0.getAngleStart()) * fraction);
+        double extent = v0.getAngleExtent() +
+            ((v1.getAngleExtent() - v0.getAngleExtent()) * fraction);
+        Arc2D value = (Arc2D)v0.clone();
+        value.setArc(x, y, w, h, start, extent, v0.getArcType());
+        return value;
+    }
+}
+
+class EvaluatorQuadCurve2D extends Evaluator<QuadCurve2D> {
+    public EvaluatorQuadCurve2D() {}
+    public QuadCurve2D evaluate(QuadCurve2D v0, QuadCurve2D v1,
+                                   float fraction)
+    {
+        double x1 = v0.getX1() + ((v1.getX1() - v0.getX1()) * fraction);
+        double y1 = v0.getY1() + ((v1.getY1() - v0.getY1()) * fraction);
+        double x2 = v0.getX2() + ((v1.getX2() - v0.getX2()) * fraction);
+        double y2 = v0.getY2() + ((v1.getY2() - v0.getY2()) * fraction);
+        double ctrlx = v0.getCtrlX() +
+            ((v1.getCtrlX() - v0.getCtrlX()) * fraction);
+        double ctrly = v0.getCtrlY() +
+            ((v1.getCtrlY() - v0.getCtrlY()) * fraction);
+        QuadCurve2D value = (QuadCurve2D)v0.clone();
+        value.setCurve(x1, y1, ctrlx, ctrly, x2, y2);
+        return value;
+    }
+}
+
+class EvaluatorCubicCurve2D extends Evaluator<CubicCurve2D> {
+    public EvaluatorCubicCurve2D() {}
+    public CubicCurve2D evaluate(CubicCurve2D v0, CubicCurve2D v1,
+                                    float fraction)
+    {
+        double x1 = v0.getX1() + ((v1.getX1() - v0.getX1()) * fraction);
+        double y1 = v0.getY1() + ((v1.getY1() - v0.getY1()) * fraction);
+        double x2 = v0.getX2() + ((v1.getX2() - v0.getX2()) * fraction);
+        double y2 = v0.getY2() + ((v1.getY2() - v0.getY2()) * fraction);
+        double ctrlx1 = v0.getCtrlX1() +
+            ((v1.getCtrlX1() - v0.getCtrlX1()) * fraction);
+        double ctrly1 = v0.getCtrlY1() +
+            ((v1.getCtrlY1() - v0.getCtrlY1()) * fraction);
+        double ctrlx2 = v0.getCtrlX2() +
+            ((v1.getCtrlX2() - v0.getCtrlX2()) * fraction);
+        double ctrly2 = v0.getCtrlY2() +
+            ((v1.getCtrlY2() - v0.getCtrlY2()) * fraction);
+        CubicCurve2D value = (CubicCurve2D)v0.clone();
+        value.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2);
+        return value;
+    }
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Interpolator.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Interpolator.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/Interpolator.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+/**
+ * Interface that defines the single {@link #interpolate(float)} method.
+ * This interface is implemented by built-in interpolators.  
+ * Applications may choose to implement
+ * their own Interpolator to get custom interpolation behavior.
+ *
+ * @author Chet
+ */
+public interface Interpolator {
+  
+    /**
+     * This function takes an input value between 0 and 1 and returns
+     * another value, also between 0 and 1. The purpose of the function
+     * is to define how time (represented as a (0-1) fraction of the
+     * duration of an animation) is altered to derive different value
+     * calculations during an animation.
+     * @param fraction a value between 0 and 1, representing the elapsed
+     * fraction of a time interval (either an entire animation cycle or an 
+     * interval between two KeyTimes, depending on where this Interpolator has
+     * been set)
+     * @return a value between 0 and 1.  Values outside of this boundary may
+     * be clamped to the interval [0,1] and cause undefined results.
+     */
+    public float interpolate(float fraction);
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyFrames.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyFrames.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyFrames.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,213 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.lang.reflect.Method;
+import org.jdesktop.animation.timing.*;
+
+/**
+ *
+ * KeyFrames holds information about the times at which values are sampled
+ * (KeyTimes) and the values at those times (KeyValues).  It also holds
+ * information about how to interpolate between these values for
+ * times that lie between the sampling points.
+ *
+ * @author Chet
+ */
+public class KeyFrames {
+    
+    private KeyValues keyValues;
+    private KeyTimes keyTimes;
+    private KeyInterpolators interpolators;
+    
+    /** 
+     * Simplest variation; determine keyTimes based on even division of
+     * 0-1 range based on number of keyValues.  This constructor
+     * assumes LINEAR interpolation.
+     * @param keyValues values that will be assumed at each time in keyTimes
+     */
+    public KeyFrames(KeyValues keyValues) {
+        init(keyValues, null, (Interpolator)null);
+    }
+    
+    /**
+     * This variant takes both keyValues (values at each
+     * point in time) and keyTimes (times at which values are sampled).
+     * @param keyValues values that the animation will assume at each of the
+     * corresponding times in keyTimes
+     * @param keyTimes times at which the animation will assume the
+     * corresponding values in keyValues
+     * @throws IllegalArgumentException keyTimes and keySizes must have the
+     * same number of elements since these structures are meant to have
+     * corresponding entries; an exception is thrown otherwise.
+     */
+    public KeyFrames(KeyValues keyValues, KeyTimes keyTimes) {
+        init(keyValues, keyTimes, (Interpolator)null);
+    }
+    
+    /**
+     * Full constructor: caller provides
+     * an instance of all key* structures which will be used to calculate
+     * between all times in the keyTimes list.  A null interpolator parameter
+     * is equivalent to calling {@link KeyFrames#KeyFrames(KeyValues, KeyTimes)}.
+     * @param keyValues values that the animation will assume at each of the
+     * corresponding times in keyTimes
+     * @param keyTimes times at which the animation will assume the
+     * corresponding values in keyValues
+     * @param interpolators collection of Interpolators that control 
+     * the calculation of values in each of the intervals defined by keyFrames.
+     * If this value is null, a {@link LinearInterpolator} will be used
+     * for all intervals.  If there is only one interpolator, that interpolator
+     * will be used for all intervals.  Otherwise, there must be a number of
+     * interpolators equal to the number of intervals (which is one less than
+     * the number of keyTimes).
+     * @throws IllegalArgumentException keyTimes and keyValues must have the
+     * same number of elements since these structures are meant to have
+     * corresponding entries; an exception is thrown otherwise.
+     * @throws IllegalArgumentException The number of interpolators must either
+     * be zero (interpolators == null), one, or one less than the size of 
+     * keyTimes.
+     */
+    public KeyFrames(KeyValues keyValues, KeyTimes keyTimes,
+            Interpolator... interpolators) {
+        init(keyValues, keyTimes, interpolators);
+    }
+
+    /**
+     * Utility constructor that assumes even division of times according to
+     * size of keyValues and interpolation according to interpolators 
+     * parameter.
+     * @param keyValues values that the animation will assume at each of the
+     * corresponding times in keyTimes
+     * @param interpolators collection of Interpolators that control 
+     * the calculation of values in each of the intervals defined by keyFrames.
+     * If this value is null, a {@link LinearInterpolator} will be used
+     * for all intervals.  If there is only one interpolator, that interpolator
+     * will be used for all intervals.  Otherwise, there must be a number of
+     * interpolators equal to the number of intervals (which is one less than
+     * the number of keyTimes).
+     * @throws IllegalArgumentException The number of interpolators must either
+     * be zero (interpolators == null), one, or one less than the size of 
+     * keyTimes.
+     */
+    public KeyFrames(KeyValues keyValues, Interpolator... interpolators) {
+        init(keyValues, null, interpolators);
+    }
+
+    /**
+     * Utility function called by constructors to perform common
+     * initialization chores
+     */
+    private void init(KeyValues keyValues, KeyTimes keyTimes,
+            Interpolator... interpolators) {
+        int numFrames = keyValues.getSize();
+        // If keyTimes null, create our own
+        if (keyTimes == null) {
+            float keyTimesArray[] = new float[numFrames];
+            float timeVal = 0.0f;
+            keyTimesArray[0] = timeVal;
+            for (int i = 1; i < (numFrames - 1); ++i) {
+                timeVal += (1.0f / (numFrames - 1));
+                keyTimesArray[i] = timeVal;
+            }
+            keyTimesArray[numFrames - 1] = 1.0f;
+            this.keyTimes = new KeyTimes(keyTimesArray);
+        } else {
+            this.keyTimes = keyTimes;
+        }
+        this.keyValues = keyValues;
+        if (numFrames != this.keyTimes.getSize()) {
+            throw new IllegalArgumentException("keyValues and keyTimes" +
+                    " must be of equal size");
+        }
+        if (interpolators != null && 
+                (interpolators.length != (numFrames - 1)) &&
+                (interpolators.length != 1)) {
+            throw new IllegalArgumentException("interpolators must be " +
+                    "either null (implying interpolation for all intervals), " +
+                    "a single interpolator (which will be used for all " +
+                    "intervals), or a number of interpolators equal to " +
+                    "one less than the number of times.");
+        }
+        this.interpolators = new KeyInterpolators(numFrames - 1, interpolators);
+    }
+        
+    Class getType() {
+        return keyValues.getType();
+    }
+    
+    KeyValues getKeyValues() {
+        return keyValues;
+    }
+    
+    KeyTimes getKeyTimes() {
+        return keyTimes;
+    }
+    
+    /**
+     * Returns time interval that contains this time fraction
+     */
+    public int getInterval(float fraction) {
+        return keyTimes.getInterval(fraction);
+    }
+    
+    /**
+     * Returns a value for the given fraction elapsed of the animation
+     * cycle.  Given the fraction, this method will determine what
+     * interval the fraction lies within, how much of that interval has
+     * elapsed, what the boundary values are (from KeyValues), what the
+     * interpolated fraction is (from the Interpolator for the interval),
+     * and what the final interpolated intermediate value is (using the
+     * appropriate Evaluator).
+     * This method will call into the Interpolator for the time interval
+     * to get the interpolated method. To ensure that future operations
+     * succeed, the value received from the interpolation will be clamped
+     * to the interval [0,1].
+     */
+    Object getValue(float fraction) {
+        // First, figure out the real fraction to use, given the
+        // interpolation type and keyTimes
+        int interval = getInterval(fraction);
+        float t0 = keyTimes.getTime(interval);
+        float t1 = keyTimes.getTime(interval + 1);
+        float t = (fraction - t0) / (t1 - t0);
+        float interpolatedT = interpolators.interpolate(interval, t);
+        // clamp to avoid problems with buggy Interpolators
+        if (interpolatedT < 0f) {
+            interpolatedT = 0f;
+        } else if (interpolatedT > 1f) {
+            interpolatedT = 1f;
+        }
+        return keyValues.getValue(interval, (interval+1), interpolatedT);
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyInterpolators.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyInterpolators.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyInterpolators.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.util.ArrayList;
+
+/**
+ *
+ * @author Chet
+ */
+class KeyInterpolators {
+    
+    private ArrayList<Interpolator> interpolators = new ArrayList<Interpolator>();
+    
+    /**
+     * Creates a new instance of KeyInterpolators
+     */
+    KeyInterpolators(int numIntervals, Interpolator... interpolators) {
+        if (interpolators == null || interpolators[0] == null) {
+            for (int i = 0; i < numIntervals; ++i) {
+                this.interpolators.add(LinearInterpolator.getInstance());
+            }
+        } else if (interpolators.length < numIntervals) {
+            for (int i = 0; i < numIntervals; ++i) {
+                this.interpolators.add(interpolators[0]);
+            }
+        } else {
+            for (int i = 0; i < numIntervals; ++i) {
+                this.interpolators.add(interpolators[i]);
+            }
+        }
+    }
+    
+    float interpolate(int interval, float fraction) {
+        return interpolators.get(interval).interpolate(fraction);
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyTimes.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyTimes.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyTimes.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.util.ArrayList;
+import org.jdesktop.animation.timing.*;
+
+/**
+ * Stores a list of times from 0 to 1 (the elapsed fraction of an animation
+ * cycle) that are used in calculating interpolated
+ * values for PropertySetter given a matching set of KeyValues and
+ * Interpolators for those time intervals.  In the simplest case, a
+ * KeyFrame will consist of just two times in KeyTimes: 0 and 1.
+ *
+ * @author Chet
+ */
+public class KeyTimes {
+    
+    private ArrayList<Float> times = new ArrayList<Float>();
+    
+    /** 
+     * Creates a new instance of KeyTimes.  Times should be in increasing
+     * order and should all be in the range [0,1], with the first value
+     * being zero and the last being 1
+     * @throws IllegalArgumentException Time values must be ordered in
+     * increasing value, the first value must be 0 and the last value
+     * must be 1
+     */
+    public KeyTimes(float... times) {
+        if (times[0] != 0) {
+            throw new IllegalArgumentException("First time value must" +
+                    " be zero");
+        }
+        if (times[times.length - 1] != 1.0f) {
+            throw new IllegalArgumentException("Last time value must" +
+                    " be one");
+        }
+        float prevTime = 0;
+        for (float time : times) {
+            if (time < prevTime) {
+                throw new IllegalArgumentException("Time values must be" +
+                        " in increasing order");
+            }
+            this.times.add(time);
+            prevTime = time;
+        }
+    }
+    
+    ArrayList getTimes() {
+        return times;
+    }
+    
+    int getSize() {
+        return times.size();
+    }
+
+    /**
+     * Returns time interval that contains this time fraction
+     */
+    int getInterval(float fraction) {
+        int prevIndex = 0;
+        for (int i = 1; i < times.size(); ++i) {
+            float time = times.get(i);
+            if (time >= fraction) { 
+                // inclusive of start time at next interval.  So fraction==1
+                // will return the final interval (times.size() - 1)
+                return prevIndex;
+            }
+            prevIndex = i;
+        }
+        return prevIndex;
+    }
+
+    float getTime(int index) {
+        return times.get(index);
+    }
+}
\ No newline at end of file

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyValues.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyValues.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/KeyValues.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,188 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.awt.Point;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Stores a list of values that correspond to the times in a {@link
+ * KeyTimes} object.  These structures are then used to create a
+ * {@link KeyFrames} object, which is then used to create a
+ * {@link PropertySetter} for the purposes of modifying an object's
+ * property over time.
+ * <p>
+ * At each of the times in {@link KeyTimes}, the property will take
+ * on the corresponding value in the KeyValues object.  Between these
+ * times, the property will take on a value based on the interpolation
+ * information stored in the KeyFrames object and the {@link 
+ * Evaluator} for the type of the values in KeyValues.
+ * <p>
+ * This class has built-in support for various known types, as defined
+ * in {@link Evaluator}.
+ * <p>
+ * For a simple example using KeyValues to create a KeyFrames and 
+ * PropertySetter object, see the class header comments in 
+ * {@link PropertySetter}.
+ * 
+ * 
+ * @author Chet
+ */
+public class KeyValues<T> {
+
+    private final List<T> values = new ArrayList<T>();
+    private final Evaluator<T> evaluator;
+    private final Class<?> type;
+    private T startValue;
+
+    /**
+     * Constructs a KeyValues object from one or more values.  The
+     * internal Evaluator is automatically determined by the
+     * type of the parameters.
+     * 
+     * @param params the values to interpolate between.  If there is only
+     * one parameter, this is assumed to be a "to" animation where the
+     * first value is dynamically determined at runtime when the animation
+     * is started.
+     * @throws IllegalArgumentException if an {@link Evaluator} cannot be 
+     * found that can interpolate between the value types supplied
+     */
+    public static <T> KeyValues<T> create(T... params) {
+        return new KeyValues(params);
+    }
+
+    /**
+     * Constructs a KeyValues object from a Evaluator
+     * and one or more values.
+     * 
+     * @param params the values to interpolate between.  If there is only
+     * one parameter, this is assumed to be a "to" animation where the
+     * first value is dynamically determined at runtime when the animation
+     * is started.
+     * @throws IllegalArgumentException if params does not have at least
+     * one value.
+     */
+    public static <T> KeyValues<T> create(Evaluator evaluator, T... params) {
+        return new KeyValues(evaluator, params);
+    }
+
+    /**
+     * Private constructor, called by factory method
+     */
+    private KeyValues(T... params) {
+        this(Evaluator.create(params.getClass().getComponentType()),
+                params);
+    }
+
+    /**
+     * Private constructor, called by factory method
+     */
+    private KeyValues(Evaluator evaluator, T... params) {
+        if (params == null) {
+            throw new IllegalArgumentException("params array cannot be null");
+        } else if (params.length == 0) {
+            throw new IllegalArgumentException(
+                "params array must have at least one element");
+        }
+        if (params.length == 1) {
+            // this is a "to" animation; set first element to null
+            values.add(null);
+        }
+        Collections.addAll(values, params);
+        this.type = params.getClass().getComponentType();
+        this.evaluator = evaluator;
+    }
+    
+    /**
+     * Returns the number of values stored in this object.
+     *
+     * @return the number of values stored in this object
+     */
+    int getSize() {
+        return values.size();
+    }
+
+    /**
+     * Returns the data type of the values stored in this object.
+     *
+     * @return a Class value representing the type of values stored in this
+     *         object
+     */
+    Class<?> getType() {
+        return this.type;
+    }
+
+    /**
+     * Called at start of animation; sets starting value in simple
+     * "to" animations.
+     */
+    void setStartValue(T startValue) {
+        if (isToAnimation()) {
+            this.startValue = startValue;
+        }
+    }
+    
+    /**
+     * Utility method for determining whether this is a "to" animation
+     * (true if the first value is null).
+     */
+    boolean isToAnimation() {
+        return (values.get(0) == null);
+    }
+
+    /**
+     * Returns value calculated from the value at the lower index, the
+     * value at the upper index, the fraction elapsed between these 
+     * endpoints, and the evaluator set up by this object at construction
+     * time.
+     */
+    T getValue(int i0, int i1, float fraction) {
+        T value;
+        T lowerValue = values.get(i0);
+        if (lowerValue == null) {
+            // "to" animation
+            lowerValue = startValue;
+        }
+        if (i0 == i1) {
+            // trivial case
+            value = lowerValue;
+        } else {
+            T v0 = lowerValue;
+            T v1 = values.get(i1);
+            value = evaluator.evaluate(v0, v1, fraction);
+        }
+        return value;
+    }
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/LinearInterpolator.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/LinearInterpolator.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/LinearInterpolator.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import org.jdesktop.animation.timing.*;
+
+/**
+ * This class implements the Interpolator interface by providing a
+ * simple interpolate function that simply returns the value that
+ * it was given. The net effect is that callers will end up calculating
+ * values linearly during intervals.
+ * <p>
+ * Because there is no variation to this class, it is a singleton and
+ * is referenced by using the {@link #getInstance} static method.
+ *
+ * @author Chet
+ */
+public final class LinearInterpolator implements Interpolator {
+    
+    private static LinearInterpolator instance = null;
+    
+    private LinearInterpolator() {}
+    
+    /**
+     * Returns the single DiscreteInterpolator object
+     */
+    public static LinearInterpolator getInstance() {
+        if (instance == null) {
+            instance = new LinearInterpolator();
+        }
+        return instance;
+    }
+    
+    /**
+     * This method always returns the value it was given, which will cause
+     * callers to calculate a linear interpolation between boundary values.
+     * @param fraction a value between 0 and 1, representing the elapsed
+     * fraction of a time interval (either an entire animation cycle or an 
+     * interval between two KeyTimes, depending on where this Interpolator has
+     * been set)
+     * @return the same value passed in as <code>fraction</code>
+     */
+    public float interpolate(float fraction) {
+        return fraction;
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/PropertySetter.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/PropertySetter.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/PropertySetter.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,376 @@
+/**
+ * Copyright (c) 2005-2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import org.jdesktop.animation.timing.Animator;
+import org.jdesktop.animation.timing.TimingTargetAdapter;
+
+/**
+ * This class enables automating animation of object properties.
+ * The class is a TimingTarget, and should be used as a target of
+ * timing events from an Animator.  These events will be used to 
+ * change a specified property over time, according to how the
+ * PropertySetter is constructed.
+ * <p>
+ * For example, here is an animation of the "background" property
+ * of some object "obj" from blue to red over a period of one second:
+ * <pre>
+ *  PropertySetter ps = new PropertySetter(obj, "background", Color.BLUE, 
+ *                                         Color.RED);
+ *  Animator anim = new Animator(1000, ps);
+ *  anim.start();
+ * </pre>
+ * Here is the same animation, created using one of the utility
+ * factory methods that returns an animator:
+ * <pre>
+ *  Animator animator = PropertySetter.createAnimator(1000, obj, "background", 
+ *                                                    Color.BLUE, Color.RED);
+ *  anim.start();
+ * </pre>
+ * <p>
+ * More complex animations can be created by passing in multiple values
+ * for the property to take on, for example:
+ * <pre>
+ *  Animator animator = PropertySetter.createAnimator(1000, obj, "background", 
+ *                                                    Color.BLUE, Color.RED, 
+ *                                                    Color.GREEN);
+ *  anim.start();
+ * </pre>
+ * It is also possible to define more involved and tightly-controlled
+ * steps in the animation, including the times between the values and
+ * how the values are interpolated by using the constructor that takes
+ * a {@link KeyFrames} object.  KeyFrames defines the fractional times at which
+ * an object takes on specific values, the values to assume at those times,
+ * and the method of interpolation between those values.  For example,
+ * here is the same animation as above, specified through KeyFrames, where the
+ * RED color will be set 10% of the way through the animation (note that
+ * we are not setting an Interpolator, so the timing intervals will use the
+ * default LinearInterpolator):
+ * <pre>
+ *  KeyValues vals = KeyValues.create(Color.BLUE, Color.RED, Color.GREEN);
+ *  KeyTimes times = new KeyTimes(0.0f, .1f, 1.0f);
+ *  KeyFrames frames = new KeyFrames(vals, times);
+ *  Animator animator = PropertySetter.createAnimator(1000, obj, "background", 
+ *                                                    frames);
+ *  anim.start();
+ * </pre>
+ *
+ * @author Chet
+ */
+public class PropertySetter extends TimingTargetAdapter {
+
+    private Object object;
+    private String propertyName;
+    private KeyFrames keyFrames;
+    private Method propertySetter;
+    private Method propertyGetter;
+
+    /**
+     * Utility method that constructs a PropertySetter and an Animator using
+     * that PropertySetter and returns the Animator
+     * @param duration the duration, in milliseconds, of the animation
+     * @param object the object whose property will be animated
+     * @param propertyName the name of the property to be animated.  For
+     * any propertyName "foo" there must be an accessible "setFoo" method
+     * on the object.  If only one value is supplied in creating the
+     * KeyValues for the keyFrames, the animation
+     * will also need a "getFoo" method.
+     * @param keyFrames the fractional times, values, and interpolation
+     * to be used in calculating the values set on the object's property.
+     * @throws IllegalArgumentException if appropriate set/get methods
+     * cannot be found for propertyName.
+     */
+    public static Animator createAnimator(int duration, Object object, 
+            String propertyName, KeyFrames keyFrames) {
+        PropertySetter ps = new PropertySetter(object, propertyName, keyFrames);
+        Animator animator = new Animator(duration, ps);
+        return animator;
+    }
+
+    /**
+     * Utility method that constructs a PropertySetter and an Animator using
+     * that PropertySetter and returns the Animator
+     * @param duration the duration, in milliseconds, of the animation
+     * @param object the object whose property will be animated
+     * @param propertyName the name of the property to be animated.  For
+     * any propertyName "foo" there must be an accessible "setFoo" method
+     * on the object.  If only one value is supplied in creating the
+     * KeyValues for the keyFrames, the animation
+     * will also need a "getFoo" method.
+     * @param params the values that the object will take on during the
+     * animation.  Internally, a KeyFrames object will be created that
+     * will use times that split the total duration evenly. Supplying
+     * only one value for params implies that this is a "to" animation
+     * whose intial value will be determined dynamically when the animation
+     * starts.
+     * @throws IllegalArgumentException if appropriate set/get methods
+     * cannot be found for propertyName.
+     */
+    public static <T> Animator createAnimator(int duration, 
+            Object object, String propertyName, T... params) {
+        PropertySetter ps = new PropertySetter(object, propertyName, params);
+        Animator animator = new Animator(duration, ps);
+        return animator;
+    }
+    
+    /**
+     * Utility method that constructs a PropertySetter and an Animator using
+     * that PropertySetter and returns the Animator
+     * 
+     * @param duration the duration, in milliseconds, of the animation
+     * @param object the object whose property will be animated
+     * @param propertyName the name of the property to be animated.  For
+     * any propertyName "foo" there must be an accessible "setFoo" method
+     * on the object.  If only one value is supplied in creating the
+     * KeyValues for the keyFrames, the animation
+     * will also need a "getFoo" method.
+     * @param evaluator KeyValues knows how to calculate intermediate values
+     * for many built-in types, but if you want to supply values in 
+     * types not understood by KeyValues, you will need to supply your
+     * own Evaluator.
+     * @param params the values that the object will take on during the
+     * animation.  Internally, a KeyFrames object will be created that
+     * will use times that split the total duration evenly. Supplying
+     * only one value for params implies that this is a "to" animation
+     * whose intial value will be determined dynamically when the animation
+     * starts.
+     * @throws IllegalArgumentException if appropriate set/get methods
+     * cannot be found for propertyName.
+     */
+    public static <T> Animator createAnimator(int duration, 
+            Object object, String propertyName, 
+            Evaluator evaluator, T... params) {
+        PropertySetter ps = new PropertySetter(object, propertyName, evaluator, 
+                params);
+        Animator animator = new Animator(duration, ps);
+        return animator;
+    }
+
+    /**
+     * Constructor for a PropertySetter where the values the propert
+     * takes on during the animation are specified in a {@link KeyFrames}
+     * object.
+     * @param object the object whose property will be animated
+     * @param propertyName the name of the property to be animated.  For
+     * any propertyName "foo" there must be an accessible "setFoo" method
+     * on the object.  If only one value is supplied in creating the
+     * KeyValues for the keyFrames, the animation
+     * will also need a "getFoo" method.
+     * @param keyFrames the fractional times, values, and interpolation
+     * to be used in calculating the values set on the object's property.
+     * @throws IllegalArgumentException if appropriate set/get methods
+     * cannot be found for propertyName.
+     */
+    public PropertySetter(Object object, String propertyName,
+            KeyFrames keyFrames) {
+        this.object = object;
+        this.propertyName = propertyName;
+        this.keyFrames = keyFrames;
+        try {
+            setupMethodInfo();
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("Bad property name (" +
+                    propertyName +"): could not find " +
+                    "an appropriate setter or getter method for that property");
+        }
+    }
+    
+    /**
+     * Constructor for a PropertySetter where the values the propert
+     * takes on during the animation are specified in a {@link KeyFrames}
+     * object.
+     * @param object the object whose property will be animated
+     * @param propertyName the name of the property to be animated.  For
+     * any propertyName "foo" there must be an accessible "setFoo" method
+     * on the object.  If only one value is supplied in params, the animation
+     * will also need a "getFoo" method.
+     * @param params the values that the object will take on during the
+     * animation.  Internally, a KeyFrames object will be created that
+     * will use times that split the total duration evenly. Supplying
+     * only one value for params implies that this is a "to" animation
+     * whose intial value will be determined dynamically when the animation
+     * starts.
+     * @throws IllegalArgumentException if appropriate set/get methods
+     * cannot be found for propertyName.
+     */
+    public <T> PropertySetter(Object object, String propertyName, T... params) {
+        this(object, propertyName, new KeyFrames(KeyValues.create(params)));
+    }
+    
+    /**
+     * Constructor for a PropertySetter where the values the propert
+     * takes on during the animation are specified in a {@link KeyFrames}
+     * object.
+     * 
+     * @param object the object whose property will be animated
+     * @param propertyName the name of the property to be animated.  For
+     * any propertyName "foo" there must be an accessible "setFoo" method
+     * on the object.  If only one value is supplied in params, the animation
+     * will also need a "getFoo" method.
+     * @param evaluator KeyValues knows how to calculate intermediate values
+     * for many built-in types, but if you want to supply values in 
+     * types not understood by KeyValues, you will need to supply your
+     * own Evaluator.
+     * @param params the values that the object will take on during the
+     * animation.  Internally, a KeyFrames object will be created that
+     * will use times that split the total duration evenly. Supplying
+     * only one value for params implies that this is a "to" animation
+     * whose intial value will be determined dynamically when the animation
+     * starts.
+     * @throws IllegalArgumentException if appropriate set/get methods
+     * cannot be found for propertyName.
+     */
+    public <T> PropertySetter(Object object, String propertyName, 
+            Evaluator evaluator, T... params) {
+        this(object, propertyName, 
+                new KeyFrames(KeyValues.create(evaluator, params)));
+    }
+    
+    /**
+     * Translates the property name used in the PropertyRange object into
+     * the appropriate Method in the Object to be modified.  This uses
+     * standard JavaBean naming convention (e.g., propertyName would
+     * become setPropertyName).
+     * @throws NoSuchMethodException if there is no method on the
+     * object with the appropriate name
+     * @throws SecurityException if the application does not have
+     * appropriate permissions to request access to the Method
+     */
+    private void setupMethodInfo() throws NoSuchMethodException {
+        try {
+            String firstChar = propertyName.substring(0, 1);
+            String remainder = propertyName.substring(1);
+            Class propertyType = getType();
+            String propertySetterName = "set" + firstChar.toUpperCase() + remainder;
+
+            PropertyDescriptor prop = new PropertyDescriptor(propertyName, object.getClass(),
+                    null, propertySetterName);
+            propertySetter = prop.getWriteMethod();
+            if (isToAnimation()) {
+                // Only need the getter for "to" animations
+                String propertyGetterName = "get" + firstChar.toUpperCase() + 
+                        remainder;
+                prop = new PropertyDescriptor(propertyName, 
+                        object.getClass(), propertyGetterName, null);
+                propertyGetter = prop.getReadMethod();
+            }
+        } catch (Exception e) {
+            throw new NoSuchMethodException("Cannot find property methods: " + e);
+        }
+    }
+    
+    //
+    // TimingTargetAdapter overrides
+    //
+    
+    /**
+     * Called by Animator to signal that the timer is about to start.
+     * The only operation performed in this method is setting an initial
+     * value for the animation if appropriate; this accounts
+     * for "to" animations, which need to start from the current value.
+     * <p>
+     * This method is not intended for use by application code.
+     */
+    public void begin() {
+        if (isToAnimation()) {
+            try {
+                setStartValue(propertyGetter.invoke(object));
+            } catch (Exception e) {
+                System.out.println("Problem setting start value on object " +
+                        object + ": " + e);
+            }
+        }
+    }
+
+    /**
+     * Called from Animator to signal a timing event.  This
+     * causes PropertySetter to invoke the property-setting method (as 
+     * specified by the propertyName in the constructor) with the
+     * appropriate value of the property given the range of values in the
+     * KeyValues object and the fraction of the timing cycle that has
+     * elapsed.
+     * <p>
+     * This method is not intended for use by application code.
+     */
+    public void timingEvent(float fraction) {
+        try {
+            setValue(object, propertySetter, fraction);
+        } catch (Exception e) {
+            System.out.println("Problem calling setValue in " +
+                    "PropertySetter.timingEvent: " + e);
+        }
+    }
+    
+    private String getPropertyName() {
+        return propertyName;
+    }
+    
+    /**
+     * Called during begin() if this is a "to" animation, to set the start
+     * value of the animation to whatever the current value is.
+     */
+    private void setStartValue(Object object) {
+        keyFrames.getKeyValues().setStartValue(object);
+    }
+    
+    /**
+     * Sets the appropriate value on the property given the current fraction
+     */
+    private void setValue(Object object, Method method, float fraction) {
+        try {
+            method.invoke(object, keyFrames.getValue(fraction));
+        } catch (Exception e) {
+            System.out.println("Problem invoking method " +
+                    propertySetter + " in object " + object + 
+                    " in setValue" + e);
+        }
+    }
+        
+    /**
+     * Returns the type used in this property setter (defers to KeyFrames
+     * for this information).
+     */
+    private Class getType() {
+        return keyFrames.getType();
+    }
+    
+    /**
+     * Utility method for determining whether this is a "to" animation
+     * (true if the first value is null).
+     */
+    private boolean isToAnimation() {
+        return (keyFrames.getKeyValues().isToAnimation());
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/SplineInterpolator.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/SplineInterpolator.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/SplineInterpolator.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,222 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.jdesktop.animation.timing.interpolation;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import org.jdesktop.animation.timing.*;
+
+/**
+ * This class interpolates fractional values using Bezier splines.  The anchor
+ * points  * for the spline are assumed to be (0, 0) and (1, 1).  Control points
+ * should all be in the range [0, 1].
+ * <p>
+ * For more information on how splines are used to interpolate, refer to the
+ * SMIL specification at http://w3c.org.
+ * <p>
+ * This class provides one simple built-in facility for non-linear
+ * interpolation.  Applications are free to define their own Interpolator
+ * implementation and use that instead when particular non-linear
+ * effects are desired.
+ *
+ * @author Chet
+ */
+public final class SplineInterpolator implements Interpolator {
+
+    // Note: (x0,y0) and (x1,y1) are implicitly (0, 0) and (1,1) respectively
+    private float x1, y1, x2, y2;
+    private ArrayList lengths = new ArrayList();
+    
+    /**
+     * Creates a new instance of SplineInterpolator with the control points
+     * defined by (x1, y1) and (x2, y2).  The anchor points are implicitly
+     * defined as (0, 0) and (1, 1).
+     * 
+     * @throws IllegalArgumentException This exception is thrown when values
+     * beyond the allowed [0,1] range are passed in
+     */
+    public SplineInterpolator(float x1, float y1, float x2, float y2) {
+        if (x1 < 0 || x1 > 1.0f ||
+                y1 < 0 || y1 > 1.0f ||
+                x2 < 0 || x2 > 1.0f ||
+                y2 < 0 || y2 > 1.0f) {
+            throw new IllegalArgumentException("Control points must be in " +
+                    "the range [0, 1]:");
+        }
+        
+        this.x1 = x1;
+        this.y1 = y1;
+        this.x2 = x2;
+        this.y2 = y2;
+        
+        // Now contruct the array of all lengths to t in [0, 1.0]
+        float prevX = 0.0f;
+        float prevY = 0.0f;
+        float prevLength = 0.0f; // cumulative length
+        for (float t = 0.01f; t <= 1.0f; t += .01f) {
+            Point2D.Float xy = getXY(t);
+            float length = prevLength + 
+                    (float)Math.sqrt((xy.x - prevX) * (xy.x - prevX) + 
+                    (xy.y - prevY) * (xy.y - prevY));
+            LengthItem lengthItem = new LengthItem(length, t);
+            lengths.add(lengthItem);
+            prevLength = length;
+            prevX = xy.x;
+            prevY = xy.y;
+        }
+        // Now calculate the fractions so that we can access the lengths
+        // array with values in [0,1].  prevLength now holds the total
+        // length of the spline.
+        for (int i = 0; i < lengths.size(); ++i) {
+            LengthItem lengthItem = (LengthItem)lengths.get(i);
+            lengthItem.setFraction(prevLength);
+        }
+    }
+    
+    /**
+     * Calculates the XY point for a given t value.
+     *
+     * The general spline equation is:
+     *   x = b0*x0 + b1*x1 + b2*x2 + b3*x3
+     *   y = b0*y0 + b1*y1 + b2*y2 + b3*y3
+     * where:
+     *   b0 = (1-t)^3
+     *   b1 = 3 * t * (1-t)^2
+     *   b2 = 3 * t^2 * (1-t)
+     *   b3 = t^3
+     * We know that (x0,y0) == (0,0) and (x1,y1) == (1,1) for our splines,
+     * so this simplifies to:
+     *   x = b1*x1 + b2*x2 + b3
+     *   y = b1*x1 + b2*x2 + b3
+     * @param t parametric value for spline calculation
+     */
+    private Point2D.Float getXY(float t) {
+        Point2D.Float xy;
+        float invT = (1 - t);
+        float b1 = 3 * t * (invT * invT);
+        float b2 = 3 * (t * t) * invT;
+        float b3 = t * t * t;
+        xy = new Point2D.Float(
+                (b1 * x1) + (b2 * x2) + b3,
+                (b1 * y1) + (b2 * y2) + b3);
+        return xy;
+    }
+    
+    /**
+     * Utility function: When we are evaluating the spline, we only care
+     * about the Y values.  See {@link getXY getXY} for the details.
+     */
+    private float getY(float t) {
+        Point2D.Float xy;
+        float invT = (1 - t);
+        float b1 = 3 * t * (invT * invT);
+        float b2 = 3 * (t * t) * invT;
+        float b3 = t * t * t;
+        return (b1 * y1) + (b2 * y2) + b3;
+    }
+    
+    /**
+     * Given a fraction of time along the spline (which we can interpret
+     * as the length along a spline), return the interpolated value of the
+     * spline.  We first calculate the t value for the length (by doing
+     * a lookup in our array of previousloy calculated values and then
+     * linearly interpolating between the nearest values) and then
+     * calculate the Y value for this t.
+     * @param lengthFraction Fraction of time in a given time interval.
+     * @return interpolated fraction between 0 and 1
+     */
+    public float interpolate(float lengthFraction) {
+        // REMIND: speed this up with binary search
+        float interpolatedT = 1.0f;
+        float prevT = 0.0f;
+        float prevLength = 0.0f;
+        for (int i = 0; i < lengths.size(); ++i) {
+            LengthItem lengthItem = (LengthItem)lengths.get(i);
+            float fraction = lengthItem.getFraction();
+            float t = lengthItem.getT();
+            if (lengthFraction <= fraction) {
+                // answer lies between last item and this one
+                float proportion = (lengthFraction - prevLength) /
+                        (fraction - prevLength);
+                interpolatedT = prevT + proportion * (t - prevT);
+                return getY(interpolatedT);
+            }
+            prevLength = fraction;
+            prevT = t;
+        }        
+        return getY(interpolatedT);
+    }
+}
+
+/**
+ * Struct used to store information about length values.  Specifically,
+ * each item stores the "length" (which can be thought of as the time
+ * elapsed along the spline path), the "t" value at this length (used to
+ * calculate the (x,y) point along the spline), and the "fraction" which
+ * is equal to the length divided by the total absolute length of the spline.
+ * After we calculate all LengthItems for a give spline, we have a list
+ * of entries which can return the t values for fractional lengths from 
+ * 0 to 1.
+ */
+class LengthItem {
+    float length;
+    float t;
+    float fraction;
+    
+    LengthItem(float length, float t, float fraction) {
+        this.length = length;
+        this.t = t;
+        this.fraction = fraction;
+    }
+
+    LengthItem(float length, float t) {
+        this.length = length;
+        this.t = t;
+    }
+    
+    public float getLength() {
+        return length;
+    }
+    
+    public float getT() {
+        return t;
+    }
+    
+    public float getFraction() {
+        return fraction;
+    }
+
+    void setFraction(float totalLength) {
+        fraction = length / totalLength;
+    }
+}
\ No newline at end of file

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/package.html
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/package.html	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/interpolation/package.html	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,16 @@
+<HTML><HEAD>
+<META http-equiv=Content-Type content="text/html; charset=windows-1252">
+<META content="MSHTML 6.00.2900.2873" name=GENERATOR></HEAD>
+<BODY bgColor=white>
+Provides a mechanism for animating object properties between different values.
+<p>
+This package provides classes for defining object properties to animate,
+via the PropertySetter class.  KeyFrames encapsulates the definition of 
+the times (using KeyTimes) and values (using KeyValues)
+to interpolate between, as well as the
+type of interpolation to use between these values.  Interpolator is an 
+interface that is implemented by DiscreteInterpolator, LinearInterpolator,
+and SplineInterpolator for built-in interpolations, but applications can
+define their own custom interpolation as well.
+</p>
+</BODY></HTML>

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/package.html
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/package.html	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/package.html	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,17 @@
+<HTML><HEAD>
+<META http-equiv=Content-Type content="text/html; charset=windows-1252">
+<META content="MSHTML 6.00.2900.2873" name=GENERATOR></HEAD>
+<BODY bgColor=white>
+Core classes of the Timing Framework; these classes provide the
+base functionality that all animations will use.
+<p>
+This package provides the fundamental capabilities of the Timing
+Framework. The core class of the entire framework is Animator, which
+is responsible for setting up and running animations.  The other
+elements of this package are TimingTarget, which is the interface used
+by Animator to report timing events during the animation, and 
+TimingTargetAdapter, which is a utility class that users may subclass
+to pick and choose the TimingTarget events they are interested in
+receiving.
+</p>
+</BODY></HTML>

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/ActionTrigger.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/ActionTrigger.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/ActionTrigger.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.lang.reflect.Method;
+import org.jdesktop.animation.timing.*;
+
+/**
+ * ActionTrigger handles action events and
+ * starts the animator when actions occur.
+ * For example, to have anim start when a button is clicked, 
+ * one might write the following:
+ * <pre>
+ *     ActionTrigger trigger = ActionTrigger.addTrigger(button, anim);
+ * </pre>
+ *
+ * @author Chet
+ */
+public class ActionTrigger extends Trigger implements ActionListener {
+    
+    /**
+     * Creates an ActionTrigger and adds it as a listener to object.
+     *
+     * @param object an object that will be used as an event source for
+     * this trigger. This object must have the method addActionListener.
+     * @param animator the Animator that start when the event occurs
+     * @return ActionTrigger the resulting trigger
+     * @throws IllegalArgumentException if object has no 
+     * <code>addActionListener()</code>
+     */
+    public static ActionTrigger addTrigger(Object object, Animator animator) {
+        ActionTrigger trigger = new ActionTrigger(animator);
+        try {
+            Method addListenerMethod = 
+                    object.getClass().getMethod("addActionListener",
+                    ActionListener.class);
+            addListenerMethod.invoke(object, trigger);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Problem adding listener" +
+                    " to object: " + e);
+        }
+        return trigger;
+    }
+    
+    /**
+     * Creates an ActionTrigger that will start the animator upon receiving
+     * any ActionEvents. It should be added to any suitable object with
+     * an addActionListener method.
+     * @param animator the Animator that start when the event occurs
+     */
+    public ActionTrigger(Animator animator) {
+        super(animator);
+    }
+    
+    /**
+     * Called by an object generating ActionEvents to which this
+     * trigger was added as an ActionListener. This starts the Animator.
+     */
+    public void actionPerformed(ActionEvent ae) {
+        fire();
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTrigger.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTrigger.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTrigger.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import javax.swing.JComponent;
+import org.jdesktop.animation.timing.*;
+
+/**
+ * FocusTrigger handles focus events
+ * and triggers an animation based on those events.
+ * For example, to have anim start when component receives an 
+ * IN event, one might write the following:
+ * <pre>
+ *     FocusTrigger trigger = 
+ *         FocusTrigger.addTrigger(component, anim, FocusTriggerEvent.IN);
+ * </pre>
+ * 
+ * 
+ * 
+ * @author Chet
+ */
+public class FocusTrigger extends Trigger implements FocusListener {
+
+    /**
+     * Creates a non-auto-reversing FocusTrigger and adds it as a FocusListener
+     * to the component.
+     *
+     * @param component component that will generate FocusEvents for this
+     * trigger
+     * @param animator the Animator that will start when the event occurs
+     * @param event the FocusTriggerEvent that will cause the action to fire
+     * @return FocusTrigger the resulting trigger
+     */
+    public static FocusTrigger addTrigger(JComponent component, 
+            Animator animator, FocusTriggerEvent event) {
+        return addTrigger(component, animator, event, false);
+    }
+    
+    /**
+     * Creates a FocusTrigger and adds it as a FocusListener
+     * to the component.
+     * 
+     * @param component component that will generate FocusEvents for this
+     * trigger
+     * @param animator the Animator that will start when the event occurs
+     * @param event the FocusTriggerEvent that will cause the action to fire
+     * @param autoReverse flag to determine whether the animator should
+     * stop and reverse based on opposite triggerEvents.
+     * @return FocusTrigger the resulting trigger
+     */
+    public static FocusTrigger addTrigger(JComponent component, 
+            Animator animator, FocusTriggerEvent event, boolean autoReverse) {
+        FocusTrigger trigger = new FocusTrigger(animator, event, autoReverse);
+        component.addFocusListener(trigger);
+        return trigger;
+    }
+    
+    /**
+     * Creates a non-auto-reversing FocusTrigger, which should be added
+     * to a Component that will generate the focus events of interest.
+     * @param animator the Animator that will start when the event occurs
+     * @param event the FocusTriggerEvent that will cause the action to fire
+     */
+    public FocusTrigger(Animator animator, FocusTriggerEvent event) {
+        this(animator, event, false);
+    }
+
+    /**
+     * Creates a FocusTrigger, which should be added
+     * to a Component that will generate the focus events of interest.
+     * @param animator the Animator that will start when the event occurs
+     * @param event the FocusTriggerEvent that will cause the action to fire
+     * @param autoReverse flag to determine whether the animator should
+     * stop and reverse based on opposite triggerEvents.
+     */
+    public FocusTrigger(Animator animator, FocusTriggerEvent event, 
+            boolean autoReverse) {
+        super(animator, event, autoReverse);
+    }
+
+    /**
+     * Called by the object which added this trigger as a FocusListener.
+     * This method starts the animator if the trigger is waiting for a 
+     * IN event.
+     */
+    public void focusGained(FocusEvent e) {
+        fire(FocusTriggerEvent.IN);
+    }
+
+    /**
+     * Called by the object which added this trigger as a FocusListener.
+     * This method starts the animator if the trigger is waiting for a 
+     * OUT event.
+     */
+    public void focusLost(FocusEvent e) {
+        fire(FocusTriggerEvent.OUT);
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTriggerEvent.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTriggerEvent.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/FocusTriggerEvent.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+/**
+ * Focus In/Out events
+ *
+ * @author Chet
+ */
+public class FocusTriggerEvent extends TriggerEvent {
+    /**
+     * Event fired when Component receives focus
+     */
+    public static final FocusTriggerEvent IN = 
+            new FocusTriggerEvent("FocusIn");
+    /**
+     * Event fired when Component loses focus
+     */
+    public static final FocusTriggerEvent OUT = 
+            new FocusTriggerEvent("FocusOut");
+
+    /**
+     * Private constructor; this helps ensure type-safe use of 
+     * pre-defined TriggerEvent objects.
+     */
+    private FocusTriggerEvent(String name) {
+        super(name);
+    }
+
+    /**
+     * This method finds the opposite of the current event.: IN ->
+     * OUT and OUT -> IN.
+     */
+    public TriggerEvent getOppositeEvent() {
+        if (this == FocusTriggerEvent.IN) {
+            return FocusTriggerEvent.OUT;
+        } else {
+            return FocusTriggerEvent.IN;
+        }
+    }
+    
+};
+    

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTrigger.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTrigger.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTrigger.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2007, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import javax.swing.JComponent;
+import org.jdesktop.animation.timing.Animator;
+
+/**
+ * MouseTrigger handles mouse events
+ * and triggers an animation based on those events.
+ * For example, to have anim start when component receives an
+ * ENTER event, one might write the following:
+ * <pre>
+ *     MouseTrigger trigger = 
+ *         MouseTrigger.addTrigger(component, anim, MouseTriggerEvent.ENTER);
+ * </pre>
+ * 
+ * 
+ * 
+ * @author Chet
+ */
+public class MouseTrigger extends Trigger implements MouseListener {
+    
+    /**
+     * Creates a non-auto-reversing MouseTrigger and adds it as a 
+     * listener to component.
+     * 
+     * @param component component that will generate MouseEvents for this
+     * trigger
+     * @param animator the Animator that will start when the event occurs
+     * @param event the MouseTriggerEvent that will cause the action to fire
+     * @return MouseTrigger the resulting trigger
+     */
+    public static MouseTrigger addTrigger(JComponent component,
+            Animator animator, MouseTriggerEvent event) {
+        return addTrigger(component, animator, event, false);
+    }
+    
+    /**
+     * Creates a MouseTrigger and adds it as a listener to component.
+     * 
+     * @param component component that will generate MouseEvents for this
+     * trigger
+     * @param animator the Animator that will start when the event occurs
+     * @param event the FocusTriggerEvent that will cause the action to fire
+     * @param autoReverse flag to determine whether the animator should
+     * stop and reverse based on opposite triggerEvents.
+     * @return FocusTrigger the resulting trigger
+     */
+    public static MouseTrigger addTrigger(JComponent component,
+            Animator animator, MouseTriggerEvent event, boolean autoReverse) {
+        MouseTrigger trigger = new MouseTrigger(animator, event, autoReverse);
+        component.addMouseListener(trigger);
+        return trigger;
+    }
+    
+    /**
+     * Creates a non-auto-reversing MouseTrigger, which should be added
+     * to a Component that will generate the mouse events of interest
+     */
+    public MouseTrigger(Animator animator, MouseTriggerEvent event) {
+        this(animator, event, false);
+    }
+
+    /**
+     * Creates a MouseTrigger, which should be added
+     * to a Component that will generate the mouse events of interest
+     */
+    public MouseTrigger(Animator animator,
+            MouseTriggerEvent event, boolean autoReverse) {
+        super(animator, event, autoReverse);
+    }
+
+    /**
+     * Called by the object which added this trigger as a MouseListener.
+     * This method starts the animator if the trigger is waiting for an
+     * ENTER event.
+     */
+    public void mouseEntered(MouseEvent e) {
+        fire(MouseTriggerEvent.ENTER);
+    }
+
+    /**
+     * Called by the object which added this trigger as a MouseListener.
+     * This method starts the animator if the trigger is waiting for an
+     * EXIT event.
+     */
+    public void mouseExited(MouseEvent e) {
+        fire(MouseTriggerEvent.EXIT);
+    }
+
+    /**
+     * Called by the object which added this trigger as a MouseListener.
+     * This method starts the animator if the trigger is waiting for a
+     * PRESS event.
+     */
+    public void mousePressed(MouseEvent e) {
+        fire(MouseTriggerEvent.PRESS);
+    }
+
+    /**
+     * Called by the object which added this trigger as a MouseListener.
+     * This method starts the animator if the trigger is waiting for a
+     * RELEASE event.
+     */
+    public void mouseReleased(MouseEvent e) {
+        fire(MouseTriggerEvent.RELEASE);
+    }
+
+    /**
+     * Called by the object which added this trigger as a MouseListener.
+     * This method starts the animator if the trigger is waiting for a
+     * CLICK event.
+     */
+    public void mouseClicked(MouseEvent e) {
+        fire(MouseTriggerEvent.CLICK);
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTriggerEvent.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTriggerEvent.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/MouseTriggerEvent.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2007, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+/**
+ * Mouse Enter/Exit/Press/Release/Click events
+ *
+ * @author Chet
+ */
+public class MouseTriggerEvent extends TriggerEvent {
+    /**
+     * Event fired when mouse enters
+     */
+    public static final MouseTriggerEvent ENTER = 
+            new MouseTriggerEvent("Entered");
+    /**
+     * Event fired when mouse exits
+     */
+    public static final MouseTriggerEvent EXIT = 
+            new MouseTriggerEvent("Exit");
+    /**
+     * Event fired when mouse button is pressed
+     */
+    public static final MouseTriggerEvent PRESS = 
+            new MouseTriggerEvent("Press");
+    /**
+     * Event fired when mouse button is released
+     */
+    public static final MouseTriggerEvent RELEASE = 
+            new MouseTriggerEvent("Release");
+    /**
+     * Event fired when mouse is clicked
+     */
+    public static final MouseTriggerEvent CLICK = 
+            new MouseTriggerEvent("Click");
+
+    /**
+     * Protected constructor; this helps ensure type-safe use of 
+     * pre-define TriggerEvent objects.
+     */
+    private MouseTriggerEvent(String name) {
+        super(name);
+    }
+
+    /**
+     * This method finds the opposite of the current event.: <BR/>
+     * ENTER -> EXIT <BR/>
+     * EXIT -> ENTER <BR/>
+     * PRESS -> RELEASE <BR/>
+     * RELEASE -> PRESS <BR/>
+     * Note that CLICK has no obvious opposite so
+     * it simply returns CLICK (this method should probably not be called
+     * for that case).
+     * 
+     */
+    public TriggerEvent getOppositeEvent() {
+        if (this == MouseTriggerEvent.ENTER) {
+            return MouseTriggerEvent.EXIT;
+        } else if (this == MouseTriggerEvent.EXIT) {
+            return MouseTriggerEvent.ENTER;
+        } else if (this == MouseTriggerEvent.PRESS) {
+            return MouseTriggerEvent.RELEASE;
+        } else if (this == MouseTriggerEvent.RELEASE) {
+            return MouseTriggerEvent.PRESS;
+        }
+        // Possible to reach here for REPEAT action (but probably should not
+        // have been called with this event)
+        return this;
+    }
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTrigger.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTrigger.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTrigger.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+import org.jdesktop.animation.timing.*;
+
+/**
+ * TimingTrigger handles timing events and starts the animator
+ * when those events occur. This class can be useful in sequencing different
+ * Animators.  For example, one Animator can be set to start when another
+ * ends using this Trigger.  For example, to have anim2 start when anim1 ends,
+ * one might write the following:
+ * <pre>
+ *     TimingTrigger trigger = 
+ *         TimingTrigger.addTrigger(anim1, anim2, TimingTriggerEvent.STOP);
+ * </pre>
+ * 
+ * 
+ * 
+ * @author Chet
+ */
+public class TimingTrigger extends Trigger implements TimingTarget {
+
+    private Animator source;
+    private TimingTriggerEvent event;
+    
+    /**
+     * Creates a non-auto-reversing TimingTrigger and adds it as a target
+     * to the source Animator.
+     * 
+     * @param source the Animator that will be listened to for events
+     * to start the target Animator
+     * @param target the Animator that will start when the event occurs
+     * @param event the TimingTriggerEvent that will cause targetAnimator
+     * to start
+     * @return TimingTrigger the resulting trigger
+     * @see org.jdesktop.animation.timing.Animator#addTarget(TimingTarget)
+     */
+    public static TimingTrigger addTrigger(Animator source, Animator target, 
+            TimingTriggerEvent event) {
+        return addTrigger(source, target, event, false);
+    }
+
+    /**
+     * Creates a TimingTrigger and adds it as a target
+     * to the source Animator.
+     * 
+     * 
+     * @param source the Animator that will be listened to for events
+     * to start the target Animator
+     * @param target the Animator that will start when the event occurs
+     * @param event the TimingTriggerEvent that will cause targetAnimator
+     * to start
+     * @param autoReverse flag to determine whether the animator should
+     * stop and reverse based on opposite triggerEvents.
+     * @return TimingTrigger the resulting trigger
+     * @see org.jdesktop.animation.timing.Animator#addTarget(TimingTarget)
+     */
+    public static TimingTrigger addTrigger(Animator source, Animator target, 
+            TimingTriggerEvent event, boolean autoReverse) {
+        TimingTrigger trigger = new TimingTrigger(target, event, autoReverse);
+        source.addTarget(trigger);
+        return trigger;
+    }
+    
+    /**
+     * Creates a non-auto-reversing TimingTrigger, which should be added
+     * to an Animator which will generate the events sent to the
+     * trigger.
+     */
+    public TimingTrigger(Animator animator, TimingTriggerEvent event) {
+        this(animator, event, false);
+    }
+    
+    /**
+     * Creates a TimingTrigger, which should be added
+     * to an Animator which will generate the events sent to the
+     * trigger.
+     */
+    public TimingTrigger(Animator animator, TimingTriggerEvent event, 
+            boolean autoReverse) {
+        super(animator, event, autoReverse);
+    }
+    
+    // 
+    // TimingTarget implementation methods
+    //
+    
+    /**
+     * Implementation of TimingTarget method; this method does nothing
+     * in this implementation since the events of TimingTrigger are limited
+     * to START, STOP, and REPEAT
+     */
+    public void timingEvent(float fraction) {}
+    
+    /**
+     * Called by Animator when starting. Sends the TimingTriggerEvent.START
+     * event to the Trigger.
+     */
+    public void begin() {
+        fire(TimingTriggerEvent.START);
+    }
+    
+    /**
+     * Called by Animator when ending. Sends the TimingTriggerEvent.STOP
+     * event to the Trigger.
+     */
+    public void end() {
+        fire(TimingTriggerEvent.STOP);
+    }
+
+    /**
+     * Called by Animator when repeating. Sends the TimingTriggerEvent.REPEAT
+     * event to the Trigger.
+     */
+    public void repeat() {
+        fire(TimingTriggerEvent.REPEAT);
+    }
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTriggerEvent.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTriggerEvent.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TimingTriggerEvent.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+/**
+ * Timing events; TimingTriggers can be set to fire when an animator 
+ * starts, stops, or repeats.
+ *
+ * @author Chet
+ */
+public class TimingTriggerEvent extends TriggerEvent {
+    /** Event fired when Animator starts */
+    public static final TimingTriggerEvent START = 
+            new TimingTriggerEvent("Start");
+    /** Event fired when Animator stops */
+    public static final TimingTriggerEvent STOP = 
+            new TimingTriggerEvent("Stop");
+    /** 
+     * Event fired when Animator finishes one cycle and starts another
+     */
+    public static final TimingTriggerEvent REPEAT = 
+            new TimingTriggerEvent("Repeat");
+
+    private TimingTriggerEvent(String name) {
+        super(name);
+    }
+
+    /**
+     * This method finds the opposite of the current event.: START -> STOP
+     * and STOP -> START.  Note that REPEAT has no obvious opposite so
+     * it simply returns REPEAT (this method should probably not be called
+     * for that case).
+     */
+    public TriggerEvent getOppositeEvent() {
+        if (this.equals(TimingTriggerEvent.START)) {
+            return TimingTriggerEvent.STOP;
+        } else if (this.equals(TimingTriggerEvent.STOP)) {
+            return TimingTriggerEvent.START;
+        }
+        // Possible to reach here for REPEAT action (but probably should not
+        // have been called with this event)
+        return this;
+    }   
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/Trigger.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/Trigger.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/Trigger.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+import org.jdesktop.animation.timing.Animator;
+
+/**
+ * This abstract class should be overridden by any class wanting to
+ * implement a new Trigger.  The subclass will define the events to trigger
+ * off of and any listeners to handle those events. That subclass will call
+ * either {@link #fire()} or {@link #fire(TriggerEvent)} to start the
+ * animator based on an event that occurred.
+ * <p>
+ * Subclasses should call one of the constructors in Trigger, according to
+ * whether they want Trigger to discern between different TriggerEvents
+ * and whether they want Trigger to auto-reverse the animation based on
+ * opposite TriggerEvents.  
+ * <p>
+ * Subclasses should call one of the <code>fire</code> methods based on
+ * whether they want Trigger to perform any event logic or simply start
+ * the animation.
+ *
+ * @author Chet
+ */
+public abstract class Trigger {
+
+    private boolean disarmed = false;
+    private Animator animator, reverseAnimator;
+    private TriggerEvent triggerEvent;
+    private boolean autoReverse = false;
+
+    /**
+     * Creates a Trigger that will start the animator when {@link #fire()}
+     * is called. Subclasses call this method to set up a simple Trigger
+     * that will be started by calling {@link #fire()}, and will have
+     * no dependency upon the specific {@link TriggerEvent} that must have
+     * occurred to start the animator.
+     * @param animator the Animator that will start when the Trigger
+     * is fired
+     */
+    protected Trigger(Animator animator) {
+        this(animator, null);
+    }
+    
+    /**
+     * Creates a Trigger that will start the animator when 
+     * {@link #fire(TriggerEvent)} is called with an event that equals
+     * triggerEvent.
+     * @param animator the Animator that will start when the Trigger
+     * is fired
+     * @param triggerEvent the TriggerEvent that must occur for this
+     * Trigger to fire
+     */
+    protected Trigger(Animator animator, TriggerEvent triggerEvent) {
+        this(animator, triggerEvent, false);
+    }
+    
+    /**
+     * Creates a Trigger that will start the animator when 
+     * {@link #fire(TriggerEvent)} is called with an event that equals
+     * triggerEvent. Also, automatically stops and reverses animator when 
+     * opposite event occurs, and stops reversing animator likewise
+     * when triggerEvent occurs.
+     * @param animator the Animator that will start when the Trigger
+     * is fired
+     * @param triggerEvent the TriggerEvent that must occur for this
+     * Trigger to fire
+     * @param autoReverse flag to determine whether the animator should
+     * stop and reverse based on opposite triggerEvents.
+     * @see TriggerEvent#getOppositeEvent()
+     */
+    protected Trigger(Animator animator, TriggerEvent triggerEvent,
+            boolean autoReverse) {
+        this.animator = animator;
+        this.triggerEvent = triggerEvent;
+        this.autoReverse = autoReverse;
+    }
+    
+    /**
+     * This method disables this Trigger and effectively noop's any actions
+     * that would otherwise occur
+     */
+    public void disarm() {
+        disarmed = true;
+    }
+
+    /**
+     * Called by subclasses to start the animator if currentEvent equals
+     * the event that the Trigger is based upon.  Also, if the Trigger is
+     * set to autoReverse, stops and reverses the animator running in the
+     * opposite direction as appropriate.
+     * @param currentEvent the {@link TriggerEvent} that just occurred, which
+     * will be compared with the TriggerEvent used to construct this Trigger
+     * and determine whether the animator should be started or reversed
+     */
+    protected void fire(TriggerEvent currentEvent) {
+        if (disarmed) {
+            return;
+        }
+        if (currentEvent == triggerEvent) {
+            // event occurred; fire the animation
+            if (autoReverse) {
+                if (animator.isRunning()) {
+                    float f = animator.getTimingFraction();
+                    animator.stop();
+                    animator.setStartFraction(f);
+                } else {
+                    animator.setStartFraction(0f);
+                }
+            }
+            if (animator.isRunning()) {
+                animator.stop();
+            }
+            animator.setStartDirection(Animator.Direction.FORWARD);
+            fire();
+        } else if (triggerEvent != null && 
+                currentEvent == triggerEvent.getOppositeEvent()) {
+            // Opposite event occurred - run reverse anim if autoReverse
+            if (autoReverse) {
+                if (animator.isRunning()) {
+                    float f = animator.getTimingFraction();
+                    animator.stop();
+                    animator.setStartFraction(f);
+                } else {
+                    animator.setStartFraction(1f - 
+                            animator.getStartFraction());
+                }
+                animator.setStartDirection(Animator.Direction.BACKWARD);
+                fire();
+            }
+        }
+    }
+    
+    /**
+     * Utility method called by subclasses to start the animator.  This variant
+     * assumes that there need be no check of the TriggerEvent that fired,
+     * which is useful for subclasses with simple events.
+     */
+    protected void fire() {
+        if (disarmed) {
+            return;
+        }
+        if (animator.isRunning()) {
+            animator.stop();
+        }
+        animator.start();
+    }
+    
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerEvent.java
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerEvent.java	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerEvent.java	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2006, Sun Microsystems, Inc
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following 
+ *     disclaimer in the documentation and/or other materials provided 
+ *     with the distribution.
+ *   * Neither the name of the TimingFramework project nor the names of its
+ *     contributors may be used to endorse or promote products derived 
+ *     from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jdesktop.animation.timing.triggers;
+
+/**
+ * Superclass for all TriggerEvents used in the Trigger classes.  The methods
+ * here are mostly protected; it is expected that callers will not use this
+ * class directly, but will instead use subclasses with pre-defined event
+ * types. The purpose of this superclass is to provide the ability for 
+ * {@link Trigger} to treat event types generically, rather than to have
+ * all even logic in the subclasses of Trigger.
+ *
+ * @author Chet
+ */
+public class TriggerEvent {
+    
+    /**
+     * The ID of events are simple strings.  It is expected that subclasses
+     * will define static objects that callers will use instead of users
+     * having to manually create TriggerEvent objects from strings directly
+     */
+    private String name;
+    
+    /**
+     * Protected constructor; this helps ensure type-safe use of 
+     * pre-define TriggerEvent objects.
+     */
+    protected TriggerEvent(String name) {
+        this.name = name;
+    }
+        
+    /**
+     * This method returns the 'opposite' event from itself. This is used by
+     * {@link Trigger} in running an auto-reversing animation, to determine 
+     * whether an opposite event has occurred (and whether to stop/reverse
+     * the animation).  Note that some events may have no opposite.
+     * Default behavior returns same event; subclasses with multiple/opposite
+     * events must override to do the right thing here.
+     */
+    public TriggerEvent getOppositeEvent() {
+        return this;
+    }
+}

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerNotes
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerNotes	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/TriggerNotes	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,135 @@
+Trigger Notes
+
+
+Want the ability to start/stop animations based on events, something like:
+
+"When X happens, start Anim1"
+
+"When Y happens, stop Anim2"
+
+Also want to start/stop animations based on other animations:
+
+"When Anim3 stops, start Anim4"
+
+Also want ability to auto-stop animations based on opposite events,
+and auto-start opposite animations likewise:
+
+"When X happens, start Anim1, stop Anim.
+When inverse(X) happens, stop Anim1
+
+Auto-reverse might be handleable through the new setDirection() API
+in Animator
+
+One of the problems with the current API is all these subclasses with
+essentially similar constructors.  It seems like I should be able to
+have the main functionality of creating a Trigger in just the Trigger
+class itself, and then have the functionality of the event-specific
+actions and listeners embedded in some other structure.
+
+something like:
+
+Trigger myTrigger = new Trigger(myAnimator, ButtonAction.MOUSEOVER,
+    TriggerAction.START);
+
+----------------
+
+12/22/06
+
+Triggers revisit: wouldn't it be nice if I could make these things actually
+work (and have a simple API) for 1.0 (and the book)?
+
+It seems like I should be able to have a *really* simple generic interface, with
+most of the current details hidden.  let's try it:
+
+
+abstract class Trigger {
+
+    protected Animator animator;
+
+    protected Trigger(Animator animator, TriggerAction action) {
+        this.animator = animator;
+        this.action = action;
+    }
+
+    protected void fire() {
+        if (action == TriggerAction.START) {
+            animator.start();
+        } else {
+            animator.stop();
+        }
+    }
+
+    public void disarm() {
+        // cancel Animator
+        // override to cancel any listeners
+    }
+}
+
+public class TimingTarget {
+
+    public TimingTarget(Animator source, TriggerAction action, TriggerEvent event, 
+            Animator animator) {
+        super(animator, action);
+        // Setup private TimingListener
+
+        }
+
+    private class TimingTriggerListener extends TimingTargetAdapter {
+        TimingTriggerEvent event;
+        protected TimingTriggerListener(TriggerAction action, TimingTriggerEvent event) {
+            super(timer, action);
+            this.event = event;
+        }
+        public void timingEvent(float fraction) {}
+        
+        public void begin() {
+            if (event == TimingTriggerEvent.START) {
+                pullTrigger();
+            }
+        }
+        public void end() {
+            if (event == TimingTriggerEvent.STOP) {
+                pullTrigger();
+            }
+        }
+        public void repeat() {
+            if (event == TimingTriggerEvent.REPEAT) {
+                pullTrigger();
+            }
+        }
+    }    
+}
+
+........
+This is working so far, but I've hit a snag with the auto-reverse functionality.
+
+Previously, it was up to the developer to set up two separate animations that
+Trigger would then add with opposite events.  In particular, it would do this:
+
+    - setup listener with animator, START, event
+    - setup listener with animator, STOP, event-opposite
+    - setup listener with reverseAnimator, START, event-opposite
+    - setup listener with reverseAnimator, STOP, event
+
+This was useful, but not particularly a great API.
+
+It seems like, with setDirection(), getFraction(), and setInitialFraction(),
+we should be able to create all of this stuff automatically.
+
+For any Trigger that wants to START on some EVENT, we should be able to do this:
+    - create an Animator (opp) that is a copy of the one supplied (anim), but with the
+    opposite direction
+    - when EVENT occurs:
+        - if (opp.isRunning()) {
+            - f = opp.getFraction()
+            - anim.setInitialFraction(f)
+            - opp.stop();
+          }
+        - anim.start()
+    - when !EVENT occurs: 
+        - if (anim.isRunning()) {
+            - f = anim.getFraction()
+            - opp.setInitialFraction(f)
+            - anim.stop();
+          }
+        - opp.start()

Added: branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/package.html
===================================================================
--- branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/package.html	                        (rev 0)
+++ branches/upstream/timingframework/current/org/jdesktop/animation/timing/triggers/package.html	2010-11-27 19:07:53 UTC (rev 13103)
@@ -0,0 +1,16 @@
+<HTML><HEAD>
+<META http-equiv=Content-Type content="text/html; charset=windows-1252">
+<META content="MSHTML 6.00.2900.2873" name=GENERATOR></HEAD>
+<BODY bgColor=white>
+Provides simple mechanism for starting Animators
+when specific events occur.  
+<p>
+This package provides classes for both using and subclassing that
+simplify the process of associating animations with events.  
+Trigger and its subclasses associate specific events (subclasses
+of TriggerEvent) with listeners (as defined in Trigger subclasses).
+These listeners are then added (by the application) to appropriate objects.
+The animations are started when a Trigger detects that a specified
+event has occurred (through its listener).
+</p>
+</BODY></HTML>




More information about the pkg-java-commits mailing list