| /******************************************************************************* |
| * 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()); |
| } |
| |
| } |