/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.animation; import java.util.Collections; import java.util.List; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Widget; public class AnimationViewer extends StructuredViewer { private static final int DEFAULT_DURATION = 125; protected static class Frame { Object element; Image image; long duration; Frame next; public Frame(Object element, Image image, long duration) { this.element = element; this.image = image; this.duration = duration; } } private class AnimationCanvas extends Canvas { public AnimationCanvas(Composite parent, int style) { super(parent, style); } public Point computeSize(int wHint, int hHint, boolean changed) { int w = wHint >= 0 ? wHint : 0; int h = hHint >= 0 ? hHint : 0; if (hasAnimatableFrames()) { Frame frame = startFrame; do { Rectangle imgBounds = frame.image.getBounds(); if (wHint < 0) w = Math.max(w, imgBounds.width); if (hHint < 0) h = Math.max(h, imgBounds.height); frame = frame.next; } while (frame != startFrame && frame != null); } if (getStaticFrame() != null) { Frame frame = getStaticFrame(); Rectangle imgBounds = frame.image.getBounds(); if (wHint < 0) w = Math.max(w, imgBounds.width); if (hHint < 0) h = Math.max(h, imgBounds.height); } return new Point(w, h); } } private Canvas canvas; private Frame startFrame; private Frame staticFrame; private Frame currentFrame; private Thread animationThread; public AnimationViewer(Composite parent, int style) { this.canvas = new AnimationCanvas(parent, style); hookControl(this.canvas); } protected void hookControl(Control control) { super.hookControl(control); control.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { paintCanvas(e); } }); } protected void inputChanged(Object input, Object oldInput) { startFrame = null; staticFrame = null; buildFrames(input); if (canvas != null && !canvas.isDisposed()) { setCurrent(getStaticFrame()); canvas.getParent().layout(); } } private void buildFrames(Object input) { IStructuredContentProvider provider = (IStructuredContentProvider) getContentProvider(); Object[] elements = provider.getElements(input); Object staticElement; if (getContentProvider() instanceof IAnimationContentProvider) { staticElement = ((IAnimationContentProvider) getContentProvider()) .getStaticElement(input, elements); staticFrame = createFrame(staticElement); } else { staticElement = null; } Frame last = null; for (int i = 0; i < elements.length; i++) { Object element = elements[i]; if (staticElement == null || !staticElement.equals(element)) { Frame frame = createFrame(element); if (frame != null) { if (startFrame == null) startFrame = frame; if (last != null) last.next = frame; last = frame; } } } if (last != null) { last.next = startFrame; } } private Frame createFrame(Object element) { if (getLabelProvider() instanceof ILabelProvider) { Image image = ((ILabelProvider) getLabelProvider()) .getImage(element); if (image != null) { long duration; if (getContentProvider() instanceof IAnimationContentProvider) { duration = ((IAnimationContentProvider) getContentProvider()) .getDuration(element); if (duration < 0) duration = DEFAULT_DURATION; else if (duration == 0) duration = 1; } else { duration = DEFAULT_DURATION; } return new Frame(element, image, duration); } } return null; } protected void paintCanvas(PaintEvent e) { Frame frame = currentFrame; if (frame == null) return; Rectangle area = canvas.getClientArea(); if (area.width == 0 || area.height == 0) return; GC gc = e.gc; Rectangle r = frame.image.getBounds(); int x = area.x + (area.width - r.width) / 2; int y = area.y + (area.height - r.height) / 2; gc.drawImage(frame.image, x, y); } protected void handleDispose(DisposeEvent event) { super.handleDispose(event); startFrame = null; staticFrame = null; currentFrame = null; animationThread = null; } public void start() { if (animationThread == null) { createAnimationThread(); } } public void stop() { if (animationThread != null) { animationThread = null; } } private synchronized void createAnimationThread() { if (animationThread != null || !hasAnimatableFrames()) return; setCurrent(getStartFrame()); animationThread = new Thread(new Runnable() { public void run() { try { loop(); } catch (Throwable e) { } if (animationThread == null && !canvas.isDisposed()) { canvas.getDisplay().syncExec(new Runnable() { public void run() { setCurrent(getStaticFrame()); } }); } } }); animationThread.setPriority(Thread.NORM_PRIORITY + 2); animationThread.setDaemon(true); animationThread.start(); } private void loop() { while (animationThread != null && !canvas.isDisposed() && hasAnimatableFrames()) { Display display = canvas.getDisplay(); if (display.isDisposed()) return; display.syncExec(new Runnable() { public void run() { if (animationThread != null && hasAnimatableFrames()) { Frame next; if (currentFrame == null) { next = getStartFrame(); } else { next = currentFrame.next; } setCurrent(next); } } }); if (currentFrame == null) return; try { Thread.sleep(currentFrame.duration); } catch (Exception e) { } } } protected boolean hasAnimatableFrames() { return startFrame != null; } protected boolean hasFrames() { return staticFrame != null || startFrame != null; } protected Frame getStartFrame() { return startFrame; } protected Frame getStaticFrame() { return staticFrame; } protected Frame getCurrentFrame() { return currentFrame; } public Image getCurrentImage() { return currentFrame == null ? null : currentFrame.image; } public Object getCurrentElement() { return currentFrame == null ? null : currentFrame.element; } private void setCurrent(Frame frame) { if (currentFrame != frame && !canvas.isDisposed()) { this.currentFrame = frame; canvas.redraw(); setSelection(new StructuredSelection(frame.element)); } } public boolean isAnimating() { return animationThread != null; } protected Widget doFindInputItem(Object element) { return canvas; } protected Widget doFindItem(Object element) { return canvas; } protected void doUpdateItem(Widget item, Object element, boolean fullMap) { } protected List getSelectionFromWidget() { return Collections.EMPTY_LIST; } protected void internalRefresh(Object element) { } public void reveal(Object element) { } protected void setSelectionToWidget(List l, boolean reveal) { } public Control getControl() { return canvas; } }