| /******************************************************************************* |
| * Copyright (c) 2005, 2010 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(); |
| } |
| |
| } |