/******************************************************************************* * Copyright (c) 2000, 2005 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 java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.Cursor; import org.eclipse.draw2d.Connection; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.geometry.Point; import org.eclipse.gef.AccessibleAnchorProvider; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.DragTracker; import org.eclipse.gef.EditPart; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.SharedCursors; import org.eclipse.gef.Tool; import org.eclipse.gef.requests.ReconnectRequest; /** * A DragTracker that moves the endpoint of a connection to another location. */ public class ConnectionEndpointTracker extends TargetingTool implements DragTracker { private static final int FLAG_SOURCE_FEEBBACK = TargetingTool.MAX_FLAG << 1; /** The max flag */ protected static final int MAX_FLAG = FLAG_SOURCE_FEEBBACK; private String commandName; private List exclusionSet; private ConnectionEditPart connectionEditPart; /** * Constructs a new ConnectionEndpointTracker for the given ConnectionEditPart. * @param cep the ConnectionEditPart */ public ConnectionEndpointTracker(ConnectionEditPart cep) { setConnectionEditPart(cep); setDisabledCursor(SharedCursors.NO); } /** * Returns a custom "plug" cursor if this tool is in the initial, drag or accessible drag * state. Otherwise defers to <code>super</code>. * @return the cursor */ protected Cursor calculateCursor() { if (isInState(STATE_INITIAL | STATE_DRAG | STATE_ACCESSIBLE_DRAG)) return getDefaultCursor(); return super.calculateCursor(); } /** * Erases source and target feedback and executes the current command. * @see DragTracker#commitDrag() */ public void commitDrag() { eraseSourceFeedback(); eraseTargetFeedback(); executeCurrentCommand(); } /** * Creates the target request, a {@link ReconnectRequest}. * @return the target request */ protected Request createTargetRequest() { ReconnectRequest request = new ReconnectRequest(getCommandName()); request.setConnectionEditPart(getConnectionEditPart()); return request; } /** * Erases feedback and sets the viewer's focus to <code>null</code>. This will remove * any focus rectangles that were painted to show the new target or source edit part. * @see Tool#deactivate() */ public void deactivate() { eraseSourceFeedback(); getCurrentViewer().setFocus(null); super.deactivate(); } /** * Erases the source feedback. */ protected void eraseSourceFeedback() { if (!getFlag(FLAG_SOURCE_FEEBBACK)) return; setFlag(FLAG_SOURCE_FEEBBACK, false); getConnectionEditPart().eraseSourceFeedback(getTargetRequest()); } /** * @see AbstractTool#getCommandName() */ protected String getCommandName() { return commandName; } /** * Returns the ConnectionEditPart's figure. * @return the connection */ protected Connection getConnection() { return (Connection)getConnectionEditPart().getFigure(); } /** * Returns the ConnectionEditPart. * @return the ConnectionEditPart */ protected ConnectionEditPart getConnectionEditPart() { return connectionEditPart; } /** * @see AbstractTool#getDebugName() */ protected String getDebugName() { return "Connection Endpoint Tool";//$NON-NLS-1$ } /** * @see org.eclipse.gef.tools.TargetingTool#getExclusionSet() */ protected Collection getExclusionSet() { if (exclusionSet == null) { exclusionSet = new ArrayList(); exclusionSet.add(getConnection()); } return exclusionSet; } /** * If currently in the drag-in-progress state, it goes into the terminal state erases * feedback and executes the current command. * @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int) */ protected boolean handleButtonUp(int button) { if (stateTransition(STATE_DRAG_IN_PROGRESS, STATE_TERMINAL)) { eraseSourceFeedback(); eraseTargetFeedback(); executeCurrentCommand(); } return true; } /** * Updates the request and the mouse target, asks to show feedback, and gets the current * command. * @return <code>true</code> */ protected boolean handleDragInProgress() { updateTargetRequest(); updateTargetUnderMouse(); showSourceFeedback(); showTargetFeedback(); setCurrentCommand(getCommand()); return true; } /** * @see org.eclipse.gef.tools.AbstractTool#handleDragStarted() */ protected boolean handleDragStarted() { stateTransition(STATE_INITIAL, STATE_DRAG_IN_PROGRESS); return false; } /** * @see org.eclipse.gef.tools.TargetingTool#handleHover() */ protected boolean handleHover() { if (isInDragInProgress()) updateAutoexposeHelper(); return true; } /** * Processes the arrow keys (to choose a different source or target edit part) and * forwardslash and backslash keys (to try to connect to another connection). * @see org.eclipse.gef.tools.AbstractTool#handleKeyDown(org.eclipse.swt.events.KeyEvent) */ protected boolean handleKeyDown(KeyEvent e) { if (acceptArrowKey(e)) { if (stateTransition(STATE_INITIAL, STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) { // When the drag first starts, set the focus Part to be one end of the connection if (isTarget()) { getCurrentViewer().setFocus(getConnectionEditPart().getTarget()); getCurrentViewer().reveal(getConnectionEditPart().getTarget()); } else { getCurrentViewer().setFocus(getConnectionEditPart().getSource()); getCurrentViewer().reveal(getConnectionEditPart().getSource()); } } int direction = 0; switch (e.keyCode) { case SWT.ARROW_DOWN : direction = PositionConstants.SOUTH; break; case SWT.ARROW_UP: direction = PositionConstants.NORTH; break; case SWT.ARROW_RIGHT: direction = isCurrentViewerMirrored() ? PositionConstants.WEST : PositionConstants.EAST; break; case SWT.ARROW_LEFT: direction = isCurrentViewerMirrored() ? PositionConstants.EAST : PositionConstants.WEST; break; } boolean consumed = false; if (direction != 0 && e.stateMask == 0) consumed = navigateNextAnchor(direction); if (!consumed) { e.stateMask |= SWT.CONTROL; e.stateMask &= ~SWT.SHIFT; if (getCurrentViewer().getKeyHandler().keyPressed(e)) { navigateNextAnchor(0); return true; } } } if (e.character == '/' || e.character == '\\') { e.stateMask |= SWT.CONTROL; if (getCurrentViewer().getKeyHandler().keyPressed(e)) { //Do not try to connect to the same connection being dragged. if (getCurrentViewer().getFocusEditPart() != getConnectionEditPart()) navigateNextAnchor(0); return true; } } return false; } private boolean isTarget() { return getCommandName() == RequestConstants.REQ_RECONNECT_TARGET; } boolean navigateNextAnchor(int direction) { EditPart focus = getCurrentViewer().getFocusEditPart(); AccessibleAnchorProvider provider; provider = (AccessibleAnchorProvider)focus.getAdapter(AccessibleAnchorProvider.class); if (provider == null) return false; List list; if (isTarget()) list = provider.getTargetAnchorLocations(); else list = provider.getSourceAnchorLocations(); Point start = getLocation(); int distance = Integer.MAX_VALUE; Point next = null; for (int i = 0; i < list.size(); i++) { Point p = (Point)list.get(i); if (p.equals(start) || (direction != 0 && (start.getPosition(p) != direction))) continue; int d = p.getDistanceOrthogonal(start); if (d < distance) { distance = d; next = p; } } if (next != null) { placeMouseInViewer(next); return true; } return false; } /** * Sets the command name. * @param newCommandName the new command name */ public void setCommandName(String newCommandName) { commandName = newCommandName; } /** * Sets the connection edit part that is being reconnected. * @param cep the connection edit part */ public void setConnectionEditPart(ConnectionEditPart cep) { this.connectionEditPart = cep; } /** * Asks the ConnectionEditPart to show source feedback. */ protected void showSourceFeedback() { getConnectionEditPart().showSourceFeedback(getTargetRequest()); setFlag(FLAG_SOURCE_FEEBBACK, true); } /** * Updates the request location. * @see org.eclipse.gef.tools.TargetingTool#updateTargetRequest() */ protected void updateTargetRequest() { ReconnectRequest request = (ReconnectRequest)getTargetRequest(); Point p = getLocation(); request.setLocation(p); } }