/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.sds.components.ui.internal.feedback;
import java.util.Date;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.Request;
import org.eclipse.gef.SnapToHelper;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.CreationFactory;
import org.eclipse.gef.tools.TargetingTool;
/**
* A custom creation tool for PointList dependend widgets. The tool produces a
* point list, by interpreting each left click as location for a new point.
*
* @author Sven Wende
*
*/
public final class PointListCreationTool extends TargetingTool {
/**
* Property to be used in AbstractTool#setProperties(java.util.Map) for
* {@link #setFactory(CreationFactory)}.
*/
public static final Object PROPERTY_CREATION_FACTORY = "factory"; //$NON-NLS-1$
/**
* Time of the last click (ms since 1970). Used to determine doubleclicks.
*/
private long _lastClick = 0;
/**
* The creation factory.
*/
private CreationFactory _factory;
/**
* The point list, which is manipulated by this tool.
*/
private PointList _points = new PointList();
/**
* A SnapToHelper.
*/
private SnapToHelper _snap2Helper;
/**
* Default constructor.
*/
public PointListCreationTool() {
super();
}
/**
* {@inheritDoc}
*/
@Override
protected void applyProperty(final Object key, final Object value) {
if (PROPERTY_CREATION_FACTORY.equals(key)) {
if (value instanceof CreationFactory) {
setFactory((CreationFactory) value);
}
return;
}
super.applyProperty(key, value);
}
/**
* {@inheritDoc}
*/
@Override
protected Request createTargetRequest() {
_points = new PointList();
CreateRequest request = new CreateRequest();
request.setFactory(getFactory());
return request;
}
/**
* {@inheritDoc}
*/
@Override
public void deactivate() {
super.deactivate();
_snap2Helper = null;
}
/**
* {@inheritDoc}
*/
@Override
protected String getCommandName() {
return REQ_CREATE;
}
/**
* Cast the target request to a CreateRequest and returns it.
*
* @return the target request as a CreateRequest
* @see TargetingTool#getTargetRequest()
*/
protected CreateRequest getCreateRequest() {
return (CreateRequest) getTargetRequest();
}
/**
* {@inheritDoc}
*/
@Override
protected String getDebugName() {
return "Creation Tool";//$NON-NLS-1$
}
/**
* Returns the creation factory used to create the new EditParts.
*
* @return the creation factory
*/
protected CreationFactory getFactory() {
return _factory;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleButtonDown(final int button) {
if (getTargetEditPart() != null) {
_snap2Helper = (SnapToHelper) getTargetEditPart().getAdapter(
SnapToHelper.class);
}
// only react on left clicks
if (button != 1) {
setState(STATE_INVALID);
return true;
}
// the tool is in progress mode, until a doubleclick occurs
setState(STATE_DRAG_IN_PROGRESS);
// determine, whether a doubleclick occured
long now = new Date().getTime();
boolean doubleClick = (now - _lastClick < 200);
_lastClick = now;
// handle clicks
if (doubleClick) {
// remove the last point, which was just created for previewing the
// next axis
_points.removePoint(_points.size() - 1);
// perform creation of the material
if (stateTransition(STATE_DRAG | STATE_DRAG_IN_PROGRESS,
STATE_TERMINAL)) {
eraseTargetFeedback();
unlockTargetEditPart();
performCreation(button);
}
// terminate
setState(STATE_TERMINAL);
handleFinished();
} else {
Point p = getSnapedLocation();
if (_points.size() == 0) {
// add a new point
_points.addPoint(p);
} else {
// override the last point, which was the "preview" point before
_points.setPoint(p, _points.size() - 1);
}
// add an additional point, which is just for previewing the next
// axis in the graphical feedback
_points.addPoint(p);
}
updateTargetRequest();
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleButtonUp(final int button) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleDragInProgress() {
if (isInState(STATE_DRAG_IN_PROGRESS)) {
updateTargetRequest();
setCurrentCommand(getCommand());
showTargetFeedback();
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleDragStarted() {
return stateTransition(STATE_DRAG, STATE_DRAG_IN_PROGRESS);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleFocusLost() {
if (isInState(STATE_DRAG | STATE_DRAG_IN_PROGRESS)) {
eraseTargetFeedback();
setState(STATE_INVALID);
handleFinished();
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleHover() {
if (isInState(STATE_INITIAL)) {
updateAutoexposeHelper();
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean handleMove() {
if (getState() != STATE_TERMINAL && getState() != STATE_INVALID) {
if (_points.size() > 0) {
// snap
PrecisionPoint location = getSnapedLocation();
// update the last point in the list to update the graphical
// feedback
_points.setPoint(location, _points.size() - 1);
}
updateTargetRequest();
updateTargetUnderMouse();
setCurrentCommand(getCommand());
showTargetFeedback();
return true;
}
return false;
}
/**
* Gets the "snapped" location based on the current location of the mouse.
*
* @return the point of the snapped location
*/
private PrecisionPoint getSnapedLocation() {
CreateRequest req = getCreateRequest();
PrecisionPoint location = new PrecisionPoint(getLocation().x,
getLocation().y);
if (_snap2Helper != null) {
_snap2Helper.snapPoint(req, PositionConstants.NORTH_WEST,
new PrecisionPoint(getLocation().x, getLocation().y),
location);
}
return location;
}
/**
* Executes the current command and selects the newly created object. The
* button that was released to cause this creation is passed in, but since
* {@link #handleButtonDown(int)} goes into the invalid state if the button
* pressed is not button 1, this will always be button 1.
*
* @param button
* the button that was pressed
*/
protected void performCreation(final int button) {
EditPartViewer viewer = getCurrentViewer();
executeCurrentCommand();
selectAddedObject(viewer);
}
/**
* Add the newly created object to the viewer's selected objects.
*
* @param viewer
* the EditPartViewer
*/
private void selectAddedObject(final EditPartViewer viewer) {
final Object model = getCreateRequest().getNewObject();
if (model == null || viewer == null) {
return;
}
Object editpart = viewer.getEditPartRegistry().get(model);
if (editpart instanceof EditPart) {
// Force the new object to get positioned in the viewer.
viewer.flush();
viewer.select((EditPart) editpart);
}
}
/**
* Sets the creation factory used to create the new edit parts.
*
* @param factory
* the factory
*/
public void setFactory(final CreationFactory factory) {
_factory = factory;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
protected void updateTargetRequest() {
CreateRequest req = getCreateRequest();
if (isInState(STATE_DRAG_IN_PROGRESS)) {
// use the rectangle, which is defined by the point lists as new
// bounds
Rectangle bounds = _points.getBounds();
req.setSize(bounds.getSize());
req.setLocation(bounds.getLocation());
req.getExtendedData().put(AbstractPolyFeedbackFactory.PROP_POINTS,
_points);
// req.getExtendedData().clear();
if (!getCurrentInput().isAltKeyDown() && _snap2Helper != null) {
PrecisionRectangle baseRect = new PrecisionRectangle(bounds);
PrecisionRectangle result = baseRect.getPreciseCopy();
_snap2Helper.snapRectangle(req, PositionConstants.NSEW,
baseRect, result);
req.setLocation(result.getLocation());
req.setSize(result.getSize());
}
} else {
req.setSize(null);
req.setLocation(getLocation());
}
}
}