blob: 088633b69d81bd9208e1892d79e5cfe88d62e8b3 [file] [log] [blame]
/*******************************************************************************
* 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.graphics.Cursor;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartListener;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.SharedCursors;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.CreateConnectionRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.CreationFactory;
/**
* The base implementation for tools which create a connection. A connection is
* a link between two existing GraphicalEditParts.
* <P>
* A connection creation tool uses a {@link CreateConnectionRequest} to perform
* the creation. This request is sent to both graphical editparts which serve as
* the "nodes" at each end of the connection. The first node clicked on is the
* source. The source is asked for a <code>Command</code> that represents
* creating the first half of the connection. This command is then passed to the
* target editpart, which is reponsible for creating the final Command that is
* executed.
*
* @author hudsonr
*/
public class AbstractConnectionCreationTool extends TargetingTool {
/**
* The state which indicates that the connection creation has begun. This
* means that the source of the connection has been identified, and the user
* is still to determine the target.
*/
protected static final int STATE_CONNECTION_STARTED = TargetingTool.MAX_STATE << 1;
/**
* The max state.
*/
protected static final int MAX_STATE = STATE_CONNECTION_STARTED;
private static final int FLAG_SOURCE_FEEDBACK = TargetingTool.MAX_FLAG << 1;
/**
* The max flag.
*/
protected static final int MAX_FLAG = FLAG_SOURCE_FEEDBACK;
private EditPart connectionSource;
private CreationFactory factory;
private EditPartViewer viewer;
private EditPartListener.Stub deactivationListener = new EditPartListener.Stub() {
public void partDeactivated(EditPart editpart) {
handleSourceDeactivated();
}
};
/**
* The default constructor
*/
public AbstractConnectionCreationTool() {
setDefaultCursor(SharedCursors.CURSOR_PLUG);
setDisabledCursor(SharedCursors.CURSOR_PLUG_NOT);
}
/**
* Constructs a new abstract creation tool with the given creation factory.
*
* @param factory
* the creation factory
*/
public AbstractConnectionCreationTool(CreationFactory factory) {
this();
setFactory(factory);
}
/**
* @see org.eclipse.gef.tools.AbstractTool#calculateCursor()
*/
protected Cursor calculateCursor() {
if (isInState(STATE_INITIAL)) {
if (getCurrentCommand() != null)
return getDefaultCursor();
}
return super.calculateCursor();
}
/**
* @see org.eclipse.gef.tools.TargetingTool#createTargetRequest()
*/
protected Request createTargetRequest() {
CreateRequest req = new CreateConnectionRequest();
req.setFactory(getFactory());
return req;
}
/**
* Erases feedback and sets fields to <code>null</code>.
*
* @see org.eclipse.gef.Tool#deactivate()
*/
public void deactivate() {
eraseSourceFeedback();
setConnectionSource(null);
super.deactivate();
setState(STATE_TERMINAL);
viewer = null;
}
/**
* Asks the source editpart to erase connection creation feedback.
*/
protected void eraseSourceFeedback() {
if (!isShowingSourceFeedback())
return;
setFlag(FLAG_SOURCE_FEEDBACK, false);
if (connectionSource != null) {
connectionSource.eraseSourceFeedback(getSourceRequest());
}
}
/**
* @see org.eclipse.gef.tools.AbstractTool#getCommandName()
*/
protected String getCommandName() {
if (isInState(STATE_CONNECTION_STARTED
| STATE_ACCESSIBLE_DRAG_IN_PROGRESS))
return REQ_CONNECTION_END;
else
return REQ_CONNECTION_START;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#getDebugName()
*/
protected String getDebugName() {
return "Connection Creation Tool";//$NON-NLS-1$
}
/**
* @see org.eclipse.gef.tools.AbstractTool#getDebugNameForState(int)
*/
protected String getDebugNameForState(int s) {
if (s == STATE_CONNECTION_STARTED
|| s == STATE_ACCESSIBLE_DRAG_IN_PROGRESS)
return "Connection Started";//$NON-NLS-1$
return super.getDebugNameForState(s);
}
/**
* Returns the creation factory that will be used with the create connection
* request.
*
* @return the creation factory
*/
protected CreationFactory getFactory() {
return factory;
}
/**
* Returns the request sent to the source node. The source node receives the
* same request that is used with the target node. The only difference is
* that at that time the request will be typed as
* {@link RequestConstants#REQ_CONNECTION_START}.
*
* @return the request used with the source node editpart
*/
protected Request getSourceRequest() {
return getTargetRequest();
}
/**
* When the button is first pressed, the source node and its command
* contribution are determined and locked in. After that time, the tool will
* be looking for the target node to complete the connection
*
* @see org.eclipse.gef.tools.AbstractTool#handleButtonDown(int)
* @param button
* which button is pressed
* @return <code>true</code> if the button down was processed
*/
protected boolean handleButtonDown(int button) {
if (isInState(STATE_INITIAL) && button == 1) {
updateTargetRequest();
updateTargetUnderMouse();
setConnectionSource(getTargetEditPart());
Command command = getCommand();
((CreateConnectionRequest) getTargetRequest())
.setSourceEditPart(getTargetEditPart());
if (command != null) {
setState(STATE_CONNECTION_STARTED);
setCurrentCommand(command);
viewer = getCurrentViewer();
}
}
if (isInState(STATE_INITIAL) && button != 1) {
setState(STATE_INVALID);
handleInvalidInput();
}
return true;
}
/**
* Unloads or resets the tool if the state is in the terminal or invalid
* state.
*
* @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
*/
protected boolean handleButtonUp(int button) {
if (isInState(STATE_TERMINAL | STATE_INVALID))
handleFinished();
return true;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleCommandStackChanged()
*/
protected boolean handleCommandStackChanged() {
if (!isInState(STATE_INITIAL)) {
if (getCurrentInput().isMouseButtonDown(1))
setState(STATE_INVALID);
else
setState(STATE_INITIAL);
handleInvalidInput();
return true;
}
return false;
}
/**
* Method that is called when the gesture to create the connection has been
* received. Subclasses may extend or override this method to do additional
* creation setup, such as prompting the user to choose an option about the
* connection being created. Returns <code>true</code> to indicate that the
* connection creation succeeded.
*
* @return <code>true</code> if the connection creation was performed
*/
protected boolean handleCreateConnection() {
eraseSourceFeedback();
Command endCommand = getCommand();
setCurrentCommand(endCommand);
executeCurrentCommand();
return true;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleDrag()
*/
protected boolean handleDrag() {
if (isInState(STATE_CONNECTION_STARTED))
return handleMove();
return false;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress()
*/
protected boolean handleDragInProgress() {
if (isInState(STATE_ACCESSIBLE_DRAG_IN_PROGRESS))
return handleMove();
return false;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleFocusLost()
*/
protected boolean handleFocusLost() {
if (isInState(STATE_CONNECTION_STARTED)) {
eraseSourceFeedback();
eraseTargetFeedback();
setState(STATE_INVALID);
handleFinished();
}
return super.handleFocusLost();
}
/**
* @see org.eclipse.gef.tools.TargetingTool#handleHover()
*/
protected boolean handleHover() {
if (isInState(STATE_CONNECTION_STARTED))
updateAutoexposeHelper();
return true;
}
/**
* @see org.eclipse.gef.tools.TargetingTool#handleInvalidInput()
*/
protected boolean handleInvalidInput() {
eraseSourceFeedback();
setConnectionSource(null);
return super.handleInvalidInput();
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleMove()
*/
protected boolean handleMove() {
if (isInState(STATE_CONNECTION_STARTED) && viewer != getCurrentViewer())
return false;
if (isInState(STATE_CONNECTION_STARTED | STATE_INITIAL
| STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) {
updateTargetRequest();
updateTargetUnderMouse();
showSourceFeedback();
showTargetFeedback();
setCurrentCommand(getCommand());
}
return true;
}
/**
* Called if the source editpart is deactivated for some reason during the
* creation process. For example, the user performs an Undo while in the
* middle of creating a connection, which undoes a prior command which
* created the source.
*/
protected void handleSourceDeactivated() {
setState(STATE_INVALID);
handleInvalidInput();
handleFinished();
}
/**
* Returns <code>true</code> if feedback is being shown.
*
* @return <code>true</code> if showing source feedback
*/
protected boolean isShowingSourceFeedback() {
return getFlag(FLAG_SOURCE_FEEDBACK);
}
/**
* Sets the source editpart for the creation
*
* @param source
* the source editpart node
*/
protected void setConnectionSource(EditPart source) {
if (connectionSource != null)
connectionSource.removeEditPartListener(deactivationListener);
connectionSource = source;
if (connectionSource != null)
connectionSource.addEditPartListener(deactivationListener);
}
/**
* Sets the creation factory used in the request.
*
* @param factory
* the factory
*/
public void setFactory(CreationFactory factory) {
this.factory = factory;
}
/**
* Sends a show feedback request to the source editpart and sets the
* feedback flag.
*/
protected void showSourceFeedback() {
if (connectionSource != null) {
connectionSource.showSourceFeedback(getSourceRequest());
}
setFlag(FLAG_SOURCE_FEEDBACK, true);
}
/**
* @see org.eclipse.gef.tools.TargetingTool#updateTargetRequest()
*/
protected void updateTargetRequest() {
CreateConnectionRequest request = (CreateConnectionRequest) getTargetRequest();
request.setType(getCommandName());
request.setLocation(getLocation());
}
}