/*
* Copyright (c) 2002-@year@, University of Maryland
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of the University of Maryland nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Piccolo was written at the Human-Computer Interaction Laboratory www.cs.umd.edu/hcil by Jesse Grosjean
* under the supervision of Ben Bederson. The Piccolo website is www.cs.umd.edu/hcil/piccolo.
*/
package edu.umd.cs.piccolo.event;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import edu.umd.cs.piccolo.activities.PActivity;
import edu.umd.cs.piccolo.util.PUtil;
/**
* <b>PDragSequenceEventHandler</b> is designed to support mouse pressed, dragged, and
* released interaction sequences. Support is also provided for running a continuous
* activity during the drag sequence.
* <P>
* PDragSequenceEventHandler should be subclassed by a concrete event handler
* that implements a particular interaction. See PPanEventHandler, PZoomEventHandler,
* and PDragEventHandler for examples.
* <P>
* @version 1.0
* @author Jesse Grosjean
*/
public abstract class PDragSequenceEventHandler extends PBasicInputEventHandler {
private double minDragStartDistance = 0;
private transient boolean isDragging = false;
private transient Point2D mousePressedCanvasPoint;
private transient PActivity dragActivity;
private transient PInputEvent dragEvent;
private transient int sequenceInitiatedButton = MouseEvent.NOBUTTON;
public PDragSequenceEventHandler() {
}
//****************************************************************
// Basics
//****************************************************************
public boolean isDragging() {
return isDragging;
}
public void setIsDragging(boolean isDragging) {
this.isDragging = isDragging;
}
public double getMinDragStartDistance() {
return minDragStartDistance;
}
/**
* Set the minimum distance that the mouse should be dragged (in screen coords)
* before a new drag sequence is initiate.
*/
public void setMinDragStartDistance(double minDistance) {
minDragStartDistance = minDistance;
}
/**
* Return the point in canvas coordinates where the mouse was last
* pressed.
*/
public Point2D getMousePressedCanvasPoint() {
if (mousePressedCanvasPoint == null) {
mousePressedCanvasPoint = new Point2D.Double();
}
return mousePressedCanvasPoint;
}
//****************************************************************
// Dragging - Methods to indicate the stages of the drag sequence.
//****************************************************************
/**
* Subclasses should override this method to get notified of the start of
* a new drag sequence. Note that that overriding methods must still
* call super.startDrag() for correct behavior.
*/
protected void startDrag(PInputEvent e) {
dragEvent = e;
startDragActivity(e);
setIsDragging(true);
e.getComponent().setInteracting(true);
}
/**
* Subclasses should override this method to get notified of the drag
* events in a drag sequence. Note that that overriding methods must still
* call super.startDrag() for correct behavior.
*/
protected void drag(PInputEvent e) {
dragEvent = e;
}
/**
* Subclasses should override this method to get notified of the end event
* in a drag sequence. Note that that overriding methods must still
* call super.startDrag() for correct behavior.
*/
protected void endDrag(PInputEvent e) {
stopDragActivity(e);
dragEvent = null;
e.getComponent().setInteracting(false);
setIsDragging(false);
}
protected boolean shouldStartDragInteraction(PInputEvent e) {
return getMousePressedCanvasPoint().distance(e.getCanvasPosition()) >= getMinDragStartDistance();
}
//****************************************************************
// Drag Activity - Used for scheduling an activity during a drag
// sequence. For example zooming and auto panning are implemented
// using this.
//****************************************************************
protected PActivity getDragActivity() {
return dragActivity;
}
protected void startDragActivity(PInputEvent aEvent) {
dragActivity = new PActivity(-1, PUtil.DEFAULT_ACTIVITY_STEP_RATE);
dragActivity.setDelegate(new PActivity.PActivityDelegate() {
public void activityStarted(PActivity activity) {
dragActivityFirstStep(dragEvent);
}
public void activityStepped(PActivity activity) {
dragActivityStep(dragEvent);
}
public void activityFinished(PActivity activity) {
dragActivityFinalStep(dragEvent);
}
});
aEvent.getCamera().getRoot().addActivity(dragActivity);
}
protected void stopDragActivity(PInputEvent aEvent) {
dragActivity.terminate();
dragActivity = null;
}
/**
* Override this method to get notified when the drag activity
* starts stepping.
*/
protected void dragActivityFirstStep(PInputEvent aEvent) {
}
/**
* During a drag sequence an activity is scheduled that runs continuously
* while the drag sequence is active. This can be used to support some
* additional behavior that is not driven directly by mouse events. For
* example PZoomEventHandler uses it for zooming and PPanEventHandler uses
* it for auto panning.
*/
protected void dragActivityStep(PInputEvent aEvent) {
}
/**
* Override this method to get notified when the drag activity
* stops stepping.
*/
protected void dragActivityFinalStep(PInputEvent aEvent) {
}
//****************************************************************
// Events - subclasses should not override these methods, instead
// override the appropriate drag method.
//****************************************************************
public void mousePressed(PInputEvent e) {
super.mousePressed(e);
if (sequenceInitiatedButton == MouseEvent.NOBUTTON) {
sequenceInitiatedButton = e.getButton();
} else {
return;
}
getMousePressedCanvasPoint().setLocation(e.getCanvasPosition());
if (!isDragging()) {
if (shouldStartDragInteraction(e)) {
startDrag(e);
}
}
}
public void mouseDragged(PInputEvent e) {
super.mouseDragged(e);
if (sequenceInitiatedButton != MouseEvent.NOBUTTON) {
if (!isDragging()) {
if (shouldStartDragInteraction(e)) {
startDrag(e);
}
return;
}
drag(e);
}
}
public void mouseReleased(PInputEvent e) {
super.mouseReleased(e);
if (sequenceInitiatedButton == e.getButton()) {
if (isDragging()) endDrag(e);
sequenceInitiatedButton = MouseEvent.NOBUTTON;
}
}
//****************************************************************
// Debugging - methods for debugging
//****************************************************************
/**
* Returns a string representing the state of this node. This method is
* intended to be used only for debugging purposes, and the content and
* format of the returned string may vary between implementations. The
* returned string may be empty but may not be <code>null</code>.
*
* @return a string representation of this node's state
*/
protected String paramString() {
StringBuffer result = new StringBuffer();
result.append("minDragStartDistance=" + minDragStartDistance);
result.append(",mousePressedCanvasPoint=" + (mousePressedCanvasPoint == null ? "null" : mousePressedCanvasPoint.toString()));
result.append(",sequenceInitiatedButton=" + sequenceInitiatedButton);
if (isDragging) result.append(",dragging");
result.append(',');
result.append(super.paramString());
return result.toString();
}
}