/*******************************************************************************
 * Copyright (c) 2000, 2005 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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.Translatable;

/**
 * A Viewport is a flexible window onto a {@link ScrollPane} and represents the visible 
 * portion of the ScrollPane.
 */
public class Viewport
	extends Figure
	implements PropertyChangeListener
{

/** ID for the view location property */
public static final String PROPERTY_VIEW_LOCATION = "viewLocation"; //$NON-NLS-1$
private IFigure view;

private boolean useTranslate = false;
private boolean trackWidth = false;
private boolean trackHeight = false;
private boolean ignoreScroll = false;
private RangeModel 
	horiztonalRangeModel = null,
	verticalRangeModel = null;

{
	setLayoutManager(new ViewportLayout());
	setHorizontalRangeModel(new DefaultRangeModel());
	setVerticalRangeModel(new DefaultRangeModel());
}

/**
 * Constructs a new Viewport with the default values.
 */
public Viewport() { }

/**
 * Constructs a new Viewport.  If <i>setting</i> is <code>true</code>, the viewport will
 * use graphics translation to paint.
 * @param setting whether to use graphics translation
 */
public Viewport(boolean setting) {
	useTranslate = setting;
}

/**
 * @see IFigure#getClientArea(Rectangle)
 */
public Rectangle getClientArea(Rectangle rect) {
	super.getClientArea(rect);
	if (useGraphicsTranslate())
		rect.translate(getViewLocation());
	return rect;
}

/**
 * Returns the view, which is the contents of the {@link ScrollPane} associated with this 
 * Viewport.
 * 
 * @return the contents
 * @since 2.0
 */
public IFigure getContents() {
	return view;
}

/**
 * Returns the RangeModel associated with the horizontal motion of this Viewport
 * @return the RangeModel
 * 
 * @since 2.0
 */
public RangeModel getHorizontalRangeModel() {
	return horiztonalRangeModel;
}

/**
 * Returns <code>true</code> if the Viewport resizes itself in the vertical direction when 
 * the  available height of its view is decreased, false otherwise. This option is turned 
 * off by default, and can be activated by calling 
 * {@link #setContentsTracksHeight(boolean)} and passing in <code>true</code>.
 * 
 * @return whether the contents tracks height
 * @since 2.0
 */
public boolean getContentsTracksHeight() {
	return trackHeight;
}

/**
 * Returns <code>true</code> if the Viewport resizes itself in the horizontal direction 
 * when the available width of its view is decreased, false otherwise. This option is
 * turned off by default, and can be activated by calling 
 * {@link #setContentsTracksWidth(boolean)} and passing in <code>true</code>.
 * 
 * @return whether the contents tracks width
 * @since 2.0
 */
public boolean getContentsTracksWidth() {
	return trackWidth;
}		

/**
 * Returns the range model associated with the vertical motion of the Viewport.
 * @return the RangeModel
 * 
 * @since 2.0
 */
public RangeModel getVerticalRangeModel() {
	return verticalRangeModel;
}

/**
 * Returns the current location of this Viewport.
 * @return the current location of this Viewport
 * 
 * @since 2.0
 */
public Point getViewLocation() {
	return new Point(
		getHorizontalRangeModel().getValue(),
		getVerticalRangeModel().getValue());
}

private void localRevalidate() {
	invalidate();
	if (getLayoutManager() != null)
		getLayoutManager().invalidate();
	getUpdateManager().addInvalidFigure(this);
}

/**
 * @see Figure#paintClientArea(Graphics)
 */
protected void paintClientArea(Graphics g) {
	if (useGraphicsTranslate()) {
		Point p = getViewLocation();
		try {
			g.translate(-p.x, -p.y);
			g.pushState();
			super.paintClientArea(g);
			g.popState();
		} finally {
			g.translate(p.x, p.y);
		}
	} else
		super.paintClientArea(g);
}

/**
 * @see org.eclipse.draw2d.Figure#isCoordinateSystem()
 */
public boolean isCoordinateSystem() {
	return useGraphicsTranslate() || super.isCoordinateSystem();
}

/**
 * Listens for either of the {@link RangeModel RangeModels} to fire a property change
 * event and updates the view accordingly.  
 * @param event the event
 */
public void propertyChange(PropertyChangeEvent event) {
	if (event.getSource() instanceof RangeModel) {
		if (RangeModel.PROPERTY_VALUE.equals(event.getPropertyName())) {
			if (!ignoreScroll) {
				localRevalidate();
				if (useGraphicsTranslate()) {
					repaint();
					fireMoved(); 
				}
			}
			firePropertyChange(PROPERTY_VIEW_LOCATION, event.getOldValue(), 
								event.getNewValue());
		}
	}
}

/**
 * Sets extents of {@link RangeModel RangeModels} to the client area of this Viewport. 
 * Sets RangeModel minimums to zero. Sets RangeModel maximums to this Viewport's 
 * height/width.
 * 
 * @since 2.0
 */
protected void readjustScrollBars() {
	if (getContents() == null)
		return;
	getVerticalRangeModel().setAll(0, getClientArea().height, 
									getContents().getBounds().height);		
	getHorizontalRangeModel().setAll(0, getClientArea().width, 
									getContents().getBounds().width);
}

/**
 * Sets this Viewport to be associated with the passed Figure.
 * 
 * @param figure the new contents
 * @since 2.0
 */
public void setContents(IFigure figure) {
	if (view == figure)
		return;
	if (view != null)
		remove(view);
	view = figure;
	if (view != null)
		add(figure);
}

/**
 * Toggles the Viewport's ability to resize itself automatically when its view is 
 * decreased in size in the vertical direction. This is disabled by default.
 * 
 * @param track whether this viewport should track its height
 * @since 2.0
 */
public void setContentsTracksHeight(boolean track) {
	trackHeight = track;
}

/**
 * Toggles the Viewport's ability to resize itself automatically when its view is 
 * decreased in size in the horizontal direction. This is disabled by default.
 * 
 * @param track whether this viewport should track its width
 * @since 2.0
 */
public void setContentsTracksWidth(boolean track) {
	trackWidth = track;
}		

/**
 * Sets the horizontal location of the Viewport's view to the passed value.
 * 
 * @param value the new horizontal location
 * @since 2.0
 */
public void setHorizontalLocation(int value) {
	setViewLocation(value, getVerticalRangeModel().getValue());
}

/**
 * Sets the horizontal range model to the passed RangeModel.
 * 
 * @param rangeModel the new horizontal range model
 * @since 2.0
 */
public void setHorizontalRangeModel(RangeModel rangeModel) {
	if (horiztonalRangeModel != null)
		horiztonalRangeModel.removePropertyChangeListener(this);
	horiztonalRangeModel = rangeModel;
	horiztonalRangeModel.addPropertyChangeListener(this);
}

/**
 * If <i>value</i> is <code>true</code>, this viewport will ignore any scrolling that 
 * occurs until this method is called again with <code>false</code>.
 *  
 * @param value whether this viewport should ignore future scrolls
 */
public void setIgnoreScroll(boolean value) {
	ignoreScroll = value;
}

/**
 * Sets the vertical location of the Viewport's view to the passed value.
 * 
 * @param value the new vertical location
 * @since 2.0
 */
public void setVerticalLocation(int value) {
	setViewLocation(getHorizontalRangeModel().getValue(), value);
}

/**
 * Sets the vertical range model to the passed RangeModel.
 * 
 * @param rangeModel the new vertical RangeModel
 * @since 2.0
 */
public void setVerticalRangeModel(RangeModel rangeModel) {
	if (verticalRangeModel != null)
		verticalRangeModel.removePropertyChangeListener(this);
	verticalRangeModel = rangeModel;
	verticalRangeModel.addPropertyChangeListener(this);
}

/**
 * Sets the location of the Viewport's view to the passed values.
 * 
 * @param x The new x coordinate of the Viewport's view.
 * @param y The new y coordinate of the Viewport's view.
 * @since 2.0
 */
public void setViewLocation(int x, int y) {
	if (getHorizontalRangeModel().getValue() != x)
		getHorizontalRangeModel().setValue(x);
	if (getVerticalRangeModel().getValue() != y)
		getVerticalRangeModel().setValue(y);
}

/**
 * Sets the location of the Viewport's view to the passed Point.
 * 
 * @param p The new location of the Viewport's view.
 * @since 2.0 
 */
public void setViewLocation(Point p) {
	setViewLocation(p.x, p.y);
}

/**
 * @see IFigure#translateFromParent(Translatable)
 */
public void translateFromParent(Translatable t) {
	if (useTranslate)
		t.performTranslate(
			getHorizontalRangeModel().getValue(),
			getVerticalRangeModel().getValue()
		);
	super.translateFromParent(t);
}

/**
 * @see IFigure#translateToParent(Translatable)
 */
public void translateToParent(Translatable t) {
	if (useTranslate)
		t.performTranslate(
			-getHorizontalRangeModel().getValue(),
			-getVerticalRangeModel().getValue()
		);
	super.translateToParent(t);
}

/**
 * Returns <code>true</code> if this viewport uses graphics translation.
 * @return whether this viewport uses graphics translation
 */
public boolean useGraphicsTranslate() {
	return useTranslate;
}

/**
 * @see IFigure#validate()
 */
public void validate() {
	super.validate();
	readjustScrollBars();
}

}
