001: /**
002: *
003: */package net.refractions.udig.project.ui;
004:
005: import java.awt.Rectangle;
006: import java.util.ArrayList;
007: import java.util.Collections;
008: import java.util.Iterator;
009: import java.util.List;
010: import java.util.SortedMap;
011: import java.util.TreeMap;
012: import java.util.concurrent.CopyOnWriteArrayList;
013:
014: import net.refractions.udig.project.internal.ProjectPlugin;
015: import net.refractions.udig.project.preferences.PreferenceConstants;
016: import net.refractions.udig.project.render.displayAdapter.IMapDisplay;
017: import net.refractions.udig.project.ui.internal.ProjectUIPlugin;
018: import net.refractions.udig.project.ui.render.displayAdapter.ViewportPane;
019:
020: import org.eclipse.core.runtime.NullProgressMonitor;
021: import org.eclipse.swt.widgets.Display;
022:
023: /**
024: * Responsible for updating the viewport when animations need their next animation frame to be displayed.
025: *
026: * @author jeichar
027: */
028: public class AnimationUpdater {
029:
030: private static boolean testing = false;
031: List<IAnimation> animations = Collections
032: .synchronizedList(new ArrayList<IAnimation>());
033: private ViewportPane display;
034: private short frameInterval;
035:
036: public AnimationUpdater(ViewportPane display, short frameInterval) {
037: this .display = display;
038: this .frameInterval = frameInterval;
039: }
040:
041: public void run() {
042: for (Iterator<IAnimation> iter = animations.iterator(); iter
043: .hasNext();) {
044: IAnimation anim = iter.next();
045: if (anim.hasNext())
046: anim.nextFrame();
047: else {
048: anim.setValid(false);
049: iter.remove();
050: }
051: if (isTesting()) {
052: try {
053: anim.run(new NullProgressMonitor());
054: } catch (Exception e) {
055: throw (RuntimeException) new RuntimeException()
056: .initCause(e);
057: }
058: }
059: }
060: if (!isTesting())
061: display.repaint();
062:
063: if (animations.size() == 0) {
064: remove(this );
065: } else {
066: final RunUpdaters next = next();
067: if (next != null) {
068: final short elapseTime;
069: if (next.frameinterval <= frameInterval) {
070: elapseTime = next.frameinterval;
071: } else {
072: elapseTime = (short) (next.frameinterval - frameInterval);
073: }
074: runTimer(elapseTime, next);
075: }
076: }
077: }
078:
079: /**
080: * Should only be used for testing
081: */
082: static boolean isTesting() {
083: return testing;
084: }
085:
086: /**
087: * should only be used for testing purposes.
088: *
089: * @param testing
090: */
091: public static void setTesting(boolean isTesting) {
092: testing = isTesting;
093: }
094:
095: private static void runTimer(final short frameInterval,
096: final RunUpdaters next) {
097: Display display = Display.getCurrent();
098: if (display == null)
099: display = Display.getDefault();
100: if (display == Display.getCurrent())
101: display.timerExec(frameInterval, next);
102: else
103: display.asyncExec(new Runnable() {
104: public void run() {
105: Display.getCurrent().timerExec(frameInterval, next);
106: }
107: });
108: }
109:
110: private RunUpdaters next() {
111: synchronized (AnimationUpdater.class) {
112: SortedMap<Short, List<AnimationUpdater>> tailMap = displayToTaskMap
113: .tailMap(frameInterval);
114: if (tailMap.isEmpty()) {
115: if (displayToTaskMap.size() > 0)
116: return new RunUpdaters(displayToTaskMap.firstKey());
117: return new RunUpdaters(frameInterval);
118: }
119:
120: return new RunUpdaters(tailMap.keySet().iterator().next());
121: }
122: }
123:
124: private static class RunUpdaters implements Runnable {
125:
126: private short frameinterval;
127:
128: public RunUpdaters(short frameInterval) {
129: this .frameinterval = frameInterval;
130: }
131:
132: public void run() {
133: synchronized (AnimationUpdater.class) {
134: List<AnimationUpdater> updaters = displayToTaskMap
135: .get(frameinterval);
136: for (AnimationUpdater updater : updaters) {
137: if (!updater.display.isDisposed()
138: && updater.display.isVisible())
139: updater.run();
140: }
141: }
142: }
143: }
144:
145: private void remove(AnimationUpdater updater) {
146: synchronized (AnimationUpdater.class) {
147: List<AnimationUpdater> hashMap = displayToTaskMap
148: .get(updater.frameInterval);
149: if (hashMap != null) {
150: hashMap.remove(updater);
151: if (hashMap.isEmpty())
152: displayToTaskMap.remove(frameInterval);
153: }
154: }
155: }
156:
157: // Locked by AnimationUpdater.class
158: private static TreeMap<Short, List<AnimationUpdater>> displayToTaskMap = new TreeMap<Short, List<AnimationUpdater>>();
159:
160: // private static final Timer TIMER = new Timer("Animation Timer", true); //$NON-NLS-1$
161: private static final short MIN_INTERVAL = 100;
162:
163: public synchronized static void runTimer(IMapDisplay mapDisplay,
164: IAnimation animation) {
165: if (!ProjectPlugin.getPlugin().getPreferenceStore().getBoolean(
166: PreferenceConstants.P_SHOW_ANIMATIONS)) {
167: return;
168: }
169:
170: if (!(mapDisplay instanceof ViewportPane)) {
171: ProjectUIPlugin
172: .log(
173: "Map Display provided is not a ViewportPane and therefore does not" //$NON-NLS-1$
174: + " support animation", new RuntimeException()); //$NON-NLS-1$
175: return;
176: }
177:
178: short frameInterval = calculateFrameInterval(animation);
179: ViewportPane viewport = (ViewportPane) mapDisplay;
180:
181: // Try to get the map from animationInterval to the AnimationUpdater for that interval
182: List<AnimationUpdater> tasks = displayToTaskMap
183: .get(frameInterval);
184: boolean requiresRun = false;
185:
186: if (tasks == null) {
187: requiresRun = true;
188: tasks = new CopyOnWriteArrayList<AnimationUpdater>();
189: displayToTaskMap.put(frameInterval, tasks);
190: }
191:
192: AnimationUpdater updater = findUpdater(frameInterval, viewport);
193: viewport.addDrawCommand(animation);
194:
195: if (updater != null) {
196: updater.getAnimations().add(animation);
197: } else {
198: AnimationUpdater task = new AnimationUpdater(
199: (ViewportPane) viewport, frameInterval);
200: task.getAnimations().add(animation);
201: if (requiresRun) {
202: runTimer(frameInterval, new RunUpdaters(frameInterval));
203: }
204: tasks.add(task);
205: }
206:
207: Rectangle bounds = animation.getValidArea();
208: if (bounds == null) {
209: viewport.repaint();
210: } else {
211: viewport.repaint(bounds.x, bounds.y, bounds.width,
212: bounds.height);
213: }
214: }
215:
216: private static AnimationUpdater findUpdater(short frameInterval2,
217: ViewportPane viewport) {
218: List<AnimationUpdater> updaters = displayToTaskMap
219: .get(frameInterval2);
220: if (updaters == null)
221: return null;
222: for (AnimationUpdater updater : updaters) {
223: if (updater.display == viewport)
224: return updater;
225: }
226: return null;
227: }
228:
229: private static short calculateFrameInterval(IAnimation animation) {
230: short frameInterval = animation.getFrameInterval();
231:
232: int i = (frameInterval - (MIN_INTERVAL * (frameInterval / MIN_INTERVAL)));
233: frameInterval = (short) (frameInterval - i);
234: if (frameInterval < MIN_INTERVAL)
235: frameInterval = MIN_INTERVAL;
236: return frameInterval;
237: }
238:
239: /**
240: * @return Returns the animations.
241: */
242: public List<IAnimation> getAnimations() {
243: return animations;
244: }
245:
246: /**
247: * @return Returns the frameInterval.
248: */
249: public short getFrameInterval() {
250: return frameInterval;
251: }
252: }
|