| /******************************************************************************* |
| * Copyright (c) 2005, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.draw2d; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * A utility for coordinating figure animations. During animation, multiple |
| * <i>animators</i> are employed to capture the <em>initial</em> and <em>final</em> states |
| * for one or more figures. The animators then playback the animation by interpolating the |
| * intermediate states for each figure using the initial and final "keyframes". |
| * <P> |
| * An animator is usually stateless and represents an specific technique. Any state |
| * information is stored by the Animation utility. Therefore, one instance can be used |
| * with multiple figures. Animators hook into the validation mechanism for figures and |
| * connections. These hooks are used to capture the states, and to intercept the typical |
| * layout process to insert the interpolated state. |
| * <P> |
| * To indicate that animation is desired, clients must call {@link #markBegin()} prior to |
| * invalidating any figures that are to be included in the animation. After this method is |
| * called, changes are made, and {@link #run()} is invoked. The run method will force a |
| * validation pass to capture the final states, and then commence the animation. The |
| * animation is synchronous and the method does not return until the animation has |
| * completed. |
| * @see LayoutAnimator |
| * @since 3.2 |
| */ |
| public class Animation { |
| |
| static class AnimPair { |
| |
| final Animator animator; |
| final IFigure figure; |
| |
| AnimPair(Animator animator, IFigure figure) { |
| this.animator = animator; |
| this.figure = figure; |
| } |
| |
| public boolean equals(Object obj) { |
| AnimPair pair = (AnimPair)obj; |
| return pair.animator == animator && pair.figure == figure; |
| } |
| |
| public int hashCode() { |
| return animator.hashCode() ^ figure.hashCode(); |
| } |
| } |
| private static final int DEFAULT_DELAY = 250; |
| private static Set figureAnimators; |
| private static Map finalStates; |
| |
| private static Map initialStates; |
| private static final int PLAYBACK = 3; |
| private static float progress; |
| private static final int RECORD_FINAL = 2; |
| |
| private static final int RECORD_INITIAL = 1; |
| private static long startTime; |
| private static int state; |
| private static Set toCapture; |
| |
| private static UpdateManager updateManager; |
| |
| private static void capture() { |
| Iterator keys = figureAnimators.iterator(); |
| while (keys.hasNext()) { |
| AnimPair pair = (AnimPair) keys.next(); |
| if (toCapture.contains(pair)) |
| pair.animator.capture(pair.figure); |
| else |
| keys.remove(); |
| } |
| } |
| |
| static void cleanup() { |
| if (figureAnimators != null) { |
| Iterator keys = figureAnimators.iterator(); |
| while (keys.hasNext()) { |
| AnimPair pair = (AnimPair) keys.next(); |
| pair.animator.tearDown(pair.figure); |
| } |
| } |
| |
| state = 0; |
| step(); |
| //Allow layout to occur normally |
| //updateManager.performUpdate(); |
| |
| initialStates = null; |
| finalStates = null; |
| figureAnimators = null; |
| updateManager = null; |
| toCapture = null; |
| state = 0; |
| } |
| |
| private static void doRun(int duration) { |
| state = RECORD_FINAL; |
| findUpdateManager(); |
| updateManager.performValidation(); |
| capture(); |
| state = PLAYBACK; |
| progress = 0.1f; |
| startTime = System.currentTimeMillis(); |
| |
| notifyPlaybackStarting(); |
| |
| while (progress != 0) { |
| step(); |
| updateManager.performUpdate(); |
| if (progress == 1.0) |
| progress = 0; |
| else { |
| int delta = (int)(System.currentTimeMillis() - startTime); |
| if (delta >= duration) |
| progress = 1f; |
| else |
| progress = 0.1f + 0.9f * delta / duration; |
| } |
| } |
| } |
| |
| private static void findUpdateManager() { |
| AnimPair pair = (AnimPair) figureAnimators.iterator().next(); |
| updateManager = pair.figure.getUpdateManager(); |
| } |
| |
| /** |
| * Returns the final animation state for the given figure. |
| * @param animator the animator for the figure |
| * @param figure the figure being animated |
| * @return the final state |
| * @since 3.2 |
| */ |
| public static Object getFinalState(Animator animator, IFigure figure) { |
| return finalStates.get(new AnimPair(animator, figure)); |
| } |
| |
| /** |
| * Returns the initial animation state for the given animator and figure. If no state was |
| * recorded, <code>null</code> is returned. |
| * @param animator the animator for the figure |
| * @param figure the figure being animated |
| * @return the initial state |
| * @since 3.2 |
| */ |
| public static Object getInitialState(Animator animator, IFigure figure) { |
| return initialStates.get(new AnimPair(animator, figure)); |
| } |
| |
| /** |
| * Returns the animation progress, where 0.0 < progress ≤ 1.0. |
| * @return the progress of the animation |
| * @since 3.2 |
| */ |
| public static float getProgress() { |
| return progress; |
| } |
| |
| static void hookAnimator(IFigure figure, Animator animator) { |
| AnimPair pair = new AnimPair(animator, figure); |
| if (figureAnimators.add(pair)) |
| animator.init(figure); |
| } |
| |
| static void hookNeedsCapture(IFigure figure, Animator animator) { |
| AnimPair pair = new AnimPair(animator, figure); |
| if (figureAnimators.contains(pair)) |
| toCapture.add(pair); |
| } |
| |
| static boolean hookPlayback(IFigure figure, Animator animator) { |
| if (toCapture.contains(new AnimPair(animator, figure))) |
| return animator.playback(figure); |
| return false; |
| } |
| |
| /** |
| * Returns <code>true</code> if animation is in progress. |
| * @return <code>true</code> when animating |
| * @since 3.2 |
| */ |
| public static boolean isAnimating() { |
| return state == PLAYBACK; |
| } |
| |
| static boolean isFinalRecording() { |
| return state == RECORD_FINAL; |
| } |
| |
| static boolean isInitialRecording() { |
| return state == RECORD_INITIAL; |
| } |
| |
| /** |
| * Marks the beginning of the animation process. If the beginning has already been marked, |
| * this has no effect. |
| * @return returns <code>true</code> if beginning was not previously marked |
| * @since 3.2 |
| */ |
| public static boolean markBegin() { |
| if (state == 0) { |
| state = RECORD_INITIAL; |
| initialStates = new HashMap(); |
| finalStates = new HashMap(); |
| figureAnimators = new HashSet(); |
| toCapture = new HashSet(); |
| return true; |
| } |
| return false; |
| } |
| |
| private static void notifyPlaybackStarting() { |
| Iterator keys = figureAnimators.iterator(); |
| while (keys.hasNext()) { |
| AnimPair pair = (AnimPair) keys.next(); |
| pair.animator.playbackStarting(pair.figure); |
| } |
| } |
| |
| static void putFinalState(Animator animator, IFigure key, Object state) { |
| finalStates.put(new AnimPair(animator, key), state); |
| } |
| |
| static void putInitialState(Animator animator, IFigure key, Object state) { |
| initialStates.put(new AnimPair(animator, key), state); |
| } |
| |
| /** |
| * Runs animation using the recommended duration: 250 milliseconds. |
| * @see #run(int) |
| * @since 3.2 |
| */ |
| public static void run() { |
| run(DEFAULT_DELAY); |
| } |
| |
| /** |
| * Captures the final states for the animation and then plays the animation. |
| * @param duration the length of animation in milliseconds |
| * @since 3.2 |
| */ |
| public static void run(int duration) { |
| if (state == 0) |
| return; |
| try { |
| if (!figureAnimators.isEmpty()) |
| doRun(duration); |
| } finally { |
| cleanup(); |
| } |
| } |
| |
| private static void step() { |
| Iterator iter = initialStates.keySet().iterator(); |
| while (iter.hasNext()) |
| ((AnimPair)iter.next()).figure.revalidate(); |
| } |
| |
| } |