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