/* ****************************************************************************** * 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.gallery; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.LayoutListener; import org.eclipse.draw2d.UpdateManager; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.xmind.gef.GraphicalViewer; import org.xmind.gef.IGraphicalViewer; import org.xmind.gef.part.IGraphicalPart; import org.xmind.gef.part.IPart; import org.xmind.gef.service.GraphicalViewerService; /** * @author Frank Shaka * */ public class NavigationAnimationService extends GraphicalViewerService implements ISelectionChangedListener { private static final int DURATION = 100; // private static final int INTERVALS = 100; private static class ItemState { public double state; public Rectangle bounds; /** * */ public ItemState(NavigationItemFigure figure) { this.state = figure.getState(); this.bounds = new Rectangle(figure.getBounds()); } } private static class ItemStates { private Map<IFigure, ItemState> states = new HashMap<IFigure, ItemState>(); public IFigure itemParent; public IFigure content; public IFigure contentParent; public Rectangle contentBounds; /** * */ public ItemStates(IFigure parent, IFigure content) { this.itemParent = parent; List children = parent.getChildren(); for (int i = 0; i < children.size(); i++) { IFigure child = (IFigure) children.get(i); states.put(child, new ItemState((NavigationItemFigure) child)); } this.content = content; this.contentParent = content.getParent(); this.contentBounds = new Rectangle(content.getBounds()); } public Iterator<IFigure> figures() { return states.keySet().iterator(); } public ItemState getState(IFigure figure) { return states.get(figure); } public void apply() { Iterator<IFigure> it = figures(); while (it.hasNext()) { IFigure figure = it.next(); ItemState state = getState(figure); ((NavigationItemFigure) figure).setState(state.state); figure.setBounds(state.bounds); } content.setBounds(contentBounds); } } private class Transition extends LayoutListener.Stub implements Runnable { private UpdateManager updateManager; private ItemStates sourceStates; private ItemStates targetStates; private long start = 0; private long end = 0; /** * */ public Transition(UpdateManager updateManager, ItemStates sourceStates, ItemStates targetStates) { this.updateManager = updateManager; this.sourceStates = sourceStates; this.targetStates = targetStates; } public void start() { sourceStates.itemParent.addLayoutListener(this); sourceStates.contentParent.addLayoutListener(this); start = System.currentTimeMillis(); end = start + DURATION; sourceStates.apply(); try { while (System.currentTimeMillis() < end) { run(); } } catch (IllegalStateException e) { } targetStates.apply(); } private double getRatio() { double time = System.currentTimeMillis(); if (time > end) return -1; double ratio = ((double) (time - start)) / DURATION; return Math.max(0, Math.min(1, ratio)); } /* * (non-Javadoc) * * @see * org.eclipse.draw2d.LayoutListener.Stub#layout(org.eclipse.draw2d. * IFigure) */ @Override public boolean layout(IFigure container) { double r = getRatio(); if (r < 0) { return false; } if (container == sourceStates.itemParent) { Rectangle b = new Rectangle(); List children = container.getChildren(); for (int i = 0; i < children.size(); i++) { IFigure child = (IFigure) children.get(i); ItemState s1 = sourceStates.getState(child); ItemState s2 = targetStates.getState(child); int x = (int) seg(s1.bounds.x, s2.bounds.x, r); int y = (int) seg(s1.bounds.y, s2.bounds.y, r); int w = (int) seg(s1.bounds.width, s2.bounds.width, r); int h = (int) seg(s1.bounds.height, s2.bounds.height, r); b.setBounds(x, y, w, h); child.setBounds(b); } } else if (container == sourceStates.contentParent) { Rectangle b = new Rectangle(); Rectangle r1 = sourceStates.contentBounds; Rectangle r2 = targetStates.contentBounds; b.x = (int) seg(r1.x, r2.x, r); b.y = (int) seg(r1.y, r2.y, r); b.width = (int) seg(r1.width, r2.width, r); b.height = (int) seg(r1.height, r2.height, r); sourceStates.content.setBounds(b); } return true; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { double r = getRatio(); if (r < 0) { throw new IllegalStateException("Animation is finished."); //$NON-NLS-1$ } changeState(r); sourceStates.itemParent.revalidate(); updateManager.performUpdate(); } private void changeState(double r) { Iterator<IFigure> it = sourceStates.figures(); while (it.hasNext()) { NavigationItemFigure figure = (NavigationItemFigure) it.next(); ItemState s1 = sourceStates.getState(figure); ItemState s2 = targetStates.getState(figure); figure.setState(seg(s1.state, s2.state, r)); } } } // private States currentStates = null; // // private Queue<Transition> transitions = new LinkedList<Transition>(); // // private Transition runningTransition = null; /** * @param viewer */ public NavigationAnimationService(IGraphicalViewer viewer) { super(viewer); } /* * (non-Javadoc) * * @see org.xmind.gef.service.AbstractViewerService#activate() */ @Override protected void activate() { getViewer().addFocusedPartChangedListener(this); } /* * (non-Javadoc) * * @see org.xmind.gef.service.AbstractViewerService#deactivate() */ @Override protected void deactivate() { getViewer().removeFocusedPartChangedListener(this); } /* * (non-Javadoc) * * @see * org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged( * org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event) { if (!isActive()) return; ItemStates sourceStates = captureStates(); if (!applySelectionChanges(event.getSelection())) return; UpdateManager updateManager = ((GraphicalViewer) getViewer()) .getLightweightSystem().getUpdateManager(); updateManager.performValidation(); ItemStates targetStates = captureStates(); new Transition(updateManager, sourceStates, targetStates).start(); } /** * @param selection */ private boolean applySelectionChanges(ISelection selection) { if (selection.isEmpty()) return false; IPart part = getViewer().findPart( ((IStructuredSelection) selection).getFirstElement()); if (part == null) return false; IPart parent = getViewer().getRootPart().getContents(); for (IPart child : parent.getChildren()) { NavigationItemFigure childFigure = (NavigationItemFigure) ((IGraphicalPart) child) .getFigure(); if (child == part) { // focused: childFigure.setState(1); childFigure.getParent().setConstraint(childFigure, childFigure); } else { // not focused: childFigure.setState(0); } } ((NavigationContentPart) getViewer().getRootPart().getContents()) .resetScrollOffset(); return true; } public ItemStates captureStates() { IGraphicalPart contentPart = (IGraphicalPart) getViewer().getRootPart() .getContents(); return new ItemStates(contentPart.getContentPane(), contentPart.getFigure()); } // private void runTransition(Transition transition) { // transitions.offer(transition); // if (runningTransition != null) // return; // runNextTransition(); // } // // private void runNextTransition() { // if (!isActive()) // return; // if (transitions.isEmpty()) // return; // runningTransition = transitions.poll(); // System.out.println("Start: " + runningTransition); // runningTransition.start(); // } // private static double seg(double min, double max, double ratio) { return min + (max - min) * ratio; } }