blob: 5b849c26e59274b2875ae4b694d2d198920b1216 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2004 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.gmf.runtime.gef.ui.internal.editparts;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.ScalableFigure;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.editparts.ZoomListener;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg;
import org.eclipse.gmf.runtime.draw2d.ui.internal.figures.AnimationModel;
/**
* @author sshaw
*
* Overriden to fully support animated zoom.
*/
public class AnimatableZoomManager
extends ZoomManager {
private int zoomAnimationStyle = ANIMATE_NEVER;
private List animationListeners = new ArrayList();
/**
* @return Returns the zoomAnimationStyle.
*/
public int getZoomAnimationStyle() {
return zoomAnimationStyle;
}
/**
* @param pane
* @param viewport
*/
public AnimatableZoomManager(ScalableFigure pane, Viewport viewport) {
super(pane, viewport);
}
/**
* Sets which zoom methods get animated.
*
* @param style the style bits determining the zoom methods to be animated.
*/
public void setZoomAnimationStyle(int style) {
zoomAnimationStyle = style;
}
/**
* Adds the given ZoomListener to this ZoomManager's list of listeners.
* @param listener the ZoomListener to be added
*/
public void addZoomListener(ZoomListener listener) {
super.addZoomListener(listener);
if (listener instanceof AnimatedZoomListener) {
animationListeners.add(listener);
}
}
/**
* Notifies listeners that the animated zoom has started.
*/
protected void fireAnimatedZoomStarted() {
Iterator iter = animationListeners.iterator();
while (iter.hasNext())
((AnimatedZoomListener)iter.next()).animatedZoomStarted();
}
/**
* Notifies listeners that the animated zoom has ended.
*/
protected void fireAnimatedZoomEnded() {
Iterator iter = animationListeners.iterator();
while (iter.hasNext())
((AnimatedZoomListener)iter.next()).animatedZoomEnded();
}
/**
* Allows implementators to zoom to a certain level centered around a given point.
*
* @param zoom <code>double</code> value where 1.0 represents 100%.
* @param center <code>Point</code> around which the zoom will be centered in absolute coordinates
*/
public void zoomTo(double zoom, Point center) {
Point centerRel = center.getCopy();
getScalableFigure().translateToRelative(centerRel);
primSetZoom(zoom, centerRel);
}
/**
* Allows implementors to zoom into or out to a rectangular area.
*
* @param rect <code>Rectangle</code> that the edit part will zoom into our out to in
* absolute coordinates.
*/
public void zoomTo(Rectangle rect) {
Dimension available = getViewport().getClientArea().getSize();
Dimension desired = rect.getSize();
double scaleX = available.width * getZoom() / desired.width;
double scaleY = available.height * getZoom() / desired.height;
double zoom = Math.min(getMaxZoom(), Math.max(getMinZoom(), Math.min(scaleX, scaleY)));
zoomTo(zoom, rect.getCenter());
}
private static final int DURATION_INCREMENT = 400;
/**
* Sets the zoom level to the given value. Min-max range check is not done.
*
* @param zoom the new zoom level
*/
protected void primSetZoom(double zoom) {
Point center = getViewport().getClientArea().getCenter();
primSetZoom(zoom, center);
}
/**
* Calculate the animation duration based on the number of zoom increments being traversed.
*
* @param zoom
* @return <code>AnimationModel</code> that is appropriate for the zoom difference between requested and the
* current zoom level.
*/
private AnimationModel calculateAnimationModel(double zoom) {
double dmod = Math.pow(zoom / getZoom(),(double)1/8);
int steps = (int)Math.round(dmod > 1 ? dmod : 1/dmod);
int duration = Math.max(DURATION_INCREMENT, steps * DURATION_INCREMENT);
AnimationModel animationModel = new AnimationModel(duration, true);
animationModel.animationStarted();
return animationModel;
}
/**
* Sets the zoom level to the given value. Min-max range check is not done.
*
* @param zoom the new zoom level
*/
private void primSetZoom(double zoom, Point finalCenterAbs) {
primAnimateSetZoom(zoom, finalCenterAbs,
getZoomAnimationStyle() == ANIMATE_ZOOM_IN_OUT ? calculateAnimationModel(zoom) : null);
}
/**
* Performs the zoom with animation.
*
* @param zoom
* @param finalCenter
* @param animationModel
*/
private void primAnimateSetZoom(double zoom, Point finalCenter, AnimationModel animationModel) {
double initialZoom = getZoom();
double finalZoom = zoom;
Point finalCenterRel = finalCenter.getCopy();
Point originalViewLocation = getViewport().getViewLocation();
Dimension centerDiff = originalViewLocation.getDifference(getViewport().getClientArea().getCenter());
Point finalViewLocation = finalCenterRel.scale(finalZoom / initialZoom).getTranslated(centerDiff);
LineSeg scrollVector = new LineSeg(originalViewLocation, finalViewLocation);
float progress = 1.0f;
if (animationModel != null) {
animationModel.animationStarted();
progress = animationModel.getProgress();
}
boolean finished = false;
fireAnimatedZoomStarted();
while(!finished) {
if (animationModel == null || animationModel.isFinished())
finished = true;
double currentZoom = initialZoom + ((finalZoom - initialZoom) * progress);
super.primSetZoom(currentZoom);
Point currentViewLocation = scrollVector.locatePoint(progress, 0, LineSeg.Sign.POSITIVE);
setViewLocation(currentViewLocation);
getViewport().getUpdateManager().performUpdate();
if (animationModel != null)
progress = animationModel.getProgress();
}
fireAnimatedZoomEnded();
}
}