/*******************************************************************************
 * Copyright (c) 2000, 2010 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.gef.tools;

import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Cursor;

import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.swt.SWT;

import org.eclipse.gef.SharedCursors;

/**
 * A subclass of the SelectionTool that allows panning by holding down the space
 * bar.
 */
public class PanningSelectionTool extends SelectionTool {

	private boolean isSpaceBarDown = false;
	private Point viewLocation;

	/**
	 * The state to indicate that the space bar has been pressed but no drag has
	 * been initiated.
	 */
	protected static final int PAN = SelectionTool.MAX_STATE << 1;

	/**
	 * The state to indicate that a pan is in progress.
	 */
	protected static final int PAN_IN_PROGRESS = PAN << 1;

	/** Max state */
	protected static final int MAX_STATE = PAN_IN_PROGRESS;

	/**
	 * Returns <code>true</code> if spacebar condition was accepted.
	 * 
	 * @param e
	 *            the key event
	 * @return true if the space bar was the key event.
	 */
	protected boolean acceptSpaceBar(KeyEvent e) {
		return (e.character == ' ' && (e.stateMask & SWT.MODIFIER_MASK) == 0);
	}

	/**
	 * @see org.eclipse.gef.tools.AbstractTool#getDebugName()
	 */
	protected String getDebugName() {
		return "Panning Tool";//$NON-NLS-1$
	}

	/**
	 * @see org.eclipse.gef.tools.AbstractTool#getDebugNameForState(int)
	 */
	protected String getDebugNameForState(int state) {
		if (state == PAN)
			return "Pan Initial"; //$NON-NLS-1$
		else if (state == PAN_IN_PROGRESS)
			return "Pan In Progress"; //$NON-NLS-1$
		return super.getDebugNameForState(state);
	}

	/**
	 * Returns the cursor used under normal conditions.
	 * 
	 * @see #setDefaultCursor(Cursor)
	 * @return the default cursor
	 */
	protected Cursor getDefaultCursor() {
		if (isInState(PAN | PAN_IN_PROGRESS))
			return SharedCursors.HAND;
		return super.getDefaultCursor();
	}

	/**
	 * @see org.eclipse.gef.tools.SelectionTool#handleButtonDown(int)
	 */
	protected boolean handleButtonDown(int which) {
		if (which == 1
				&& getCurrentViewer().getControl() instanceof FigureCanvas
				&& stateTransition(PAN, PAN_IN_PROGRESS)) {
			viewLocation = ((FigureCanvas) getCurrentViewer().getControl())
					.getViewport().getViewLocation();
			return true;
		}
		return super.handleButtonDown(which);
	}

	/**
	 * @see org.eclipse.gef.tools.SelectionTool#handleButtonUp(int)
	 */
	protected boolean handleButtonUp(int which) {
		if (which == 1 && isSpaceBarDown
				&& stateTransition(PAN_IN_PROGRESS, PAN))
			return true;
		else if (which == 1 && stateTransition(PAN_IN_PROGRESS, STATE_INITIAL)) {
			refreshCursor();
			return true;
		}

		return super.handleButtonUp(which);
	}

	/**
	 * @see org.eclipse.gef.tools.AbstractTool#handleDrag()
	 */
	protected boolean handleDrag() {
		if (isInState(PAN_IN_PROGRESS)
				&& getCurrentViewer().getControl() instanceof FigureCanvas) {
			FigureCanvas canvas = (FigureCanvas) getCurrentViewer()
					.getControl();
			canvas.scrollTo(viewLocation.x - getDragMoveDelta().width,
					viewLocation.y - getDragMoveDelta().height);
			return true;
		} else {
			return super.handleDrag();
		}
	}

	/**
	 * @see org.eclipse.gef.tools.SelectionTool#handleFocusLost()
	 */
	protected boolean handleFocusLost() {
		if (isInState(PAN | PAN_IN_PROGRESS)) {
			setState(STATE_INITIAL);
			refreshCursor();
			return true;
		}
		return super.handleFocusLost();
	}

	/**
	 * @see org.eclipse.gef.tools.SelectionTool#handleKeyDown(org.eclipse.swt.events.KeyEvent)
	 */
	protected boolean handleKeyDown(KeyEvent e) {
		if (acceptSpaceBar(e)) {
			isSpaceBarDown = true;
			if (stateTransition(STATE_INITIAL, PAN))
				refreshCursor();
			return true;
		} else {
			if (stateTransition(PAN, STATE_INITIAL)) {
				refreshCursor();
				isSpaceBarDown = false;
				return true;
			} else if (isInState(PAN_IN_PROGRESS))
				isSpaceBarDown = false;
		}

		return super.handleKeyDown(e);
	}

	/**
	 * @see org.eclipse.gef.tools.SelectionTool#handleKeyUp(org.eclipse.swt.events.KeyEvent)
	 */
	protected boolean handleKeyUp(KeyEvent e) {
		if (acceptSpaceBar(e)) {
			isSpaceBarDown = false;
			if (stateTransition(PAN, STATE_INITIAL))
				refreshCursor();
			return true;
		}

		return super.handleKeyUp(e);
	}

}