/******************************************************************************* * Copyright (c) 2000, 2010, 2012 IBM Corporation, Gerhardt Informatics Kft. 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 * Gerhardt Informatics Kft. - GEFGWT port *******************************************************************************/ package org.eclipse.gef.tools; import java.util.Collection; import java.util.Collections; import org.eclipse.draw2d.geometry.Point; import org.eclipse.gef.AutoexposeHelper; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.UnexecutableCommand; import org.eclipse.gef.requests.TargetRequest; import org.eclipse.swt.widgets.Display; /** * The base implementation for tools which perform targeting of editparts. * Targeting tools may operate using either mouse drags or just mouse moves. * Targeting tools work with a <i>target</i> request. This request is used along * with the mouse location to obtain an active target from the current * EditPartViewer. This target is then asked for the <code>Command</code> that * performs the given request. The target is also asked to show target feedback. * <P> * TargetingTool also provides support for auto-expose (a.k.a. auto-scrolling). * Subclasses that wish to commence auto-expose can do so by calling * {@link #updateAutoexposeHelper()}. An an AutoExposeHelper is found, * auto-scrolling begins. Whenever that helper scrolls the diagram of performs * any other change, <code>handleMove</code> will be called as if the mouse had * moved. This is because the target has probably moved, but there is no input * event to trigger an update of the operation. */ public abstract class TargetingTool extends AbstractTool { private static final int FLAG_LOCK_TARGET = AbstractTool.MAX_FLAG << 1; private static final int FLAG_TARGET_FEEDBACK = AbstractTool.MAX_FLAG << 2; /** * The max flag. */ protected static final int MAX_FLAG = FLAG_TARGET_FEEDBACK; private Request targetRequest; private EditPart targetEditPart; private AutoexposeHelper exposeHelper; /** * Creates the target request that will be used with the target editpart. * This request will be cached and updated as needed. * * @see #getTargetRequest() * @return the new target request */ protected Request createTargetRequest() { Request request = new Request(); request.setType(getCommandName()); return request; } /** * @see org.eclipse.gef.Tool#deactivate() */ public void deactivate() { if (isHoverActive()) resetHover(); eraseTargetFeedback(); targetEditPart = null; targetRequest = null; setAutoexposeHelper(null); super.deactivate(); } /** * Called to perform an iteration of the autoexpose process. If the expose * helper is set, it will be asked to step at the current mouse location. If * it returns true, another expose iteration will be queued. There is no * delay between autoexpose events, other than the time required to perform * the step(). */ protected void doAutoexpose() { if (exposeHelper == null) return; if (exposeHelper.step(getLocation())) { handleAutoexpose(); Display.getCurrent().asyncExec(new QueuedAutoexpose()); } else setAutoexposeHelper(null); } /** * Asks the current target editpart to erase target feedback using the * target request. If target feedback is not being shown, this method does * nothing and returns. Otherwise, the target feedback flag is reset to * false, and the target editpart is asked to erase target feedback. This * methods should rarely be overridden. */ protected void eraseTargetFeedback() { if (!isShowingTargetFeedback()) return; setFlag(FLAG_TARGET_FEEDBACK, false); if (getTargetEditPart() != null) getTargetEditPart().eraseTargetFeedback(getTargetRequest()); } /** * Queries the target editpart for a command. * * @see org.eclipse.gef.tools.AbstractTool#getCommand() */ protected Command getCommand() { if (getTargetEditPart() == null) return null; return getTargetEditPart().getCommand(getTargetRequest()); } /** * Returns a List of objects that should be excluded as potential targets * for the operation. * * @return the list of objects to be excluded as targets */ protected Collection getExclusionSet() { return Collections.EMPTY_LIST; } /** * Returns the conditional object used for obtaining the target editpart * from the current viewer. By default, a conditional is returned that tests * whether an editpart at the current mouse location indicates a target for * the operation's request, using * {@link EditPart#getTargetEditPart(Request)}. If <code>null</code> is * returned, then the conditional fails, and the search continues. * * @see EditPartViewer#findObjectAtExcluding(Point, Collection, * EditPartViewer.Conditional) * @return the targeting conditional */ protected EditPartViewer.Conditional getTargetingConditional() { return new EditPartViewer.Conditional() { public boolean evaluate(EditPart editpart) { return editpart.getTargetEditPart(getTargetRequest()) != null; } }; } /** * Returns <code>null</code> or the current target editpart. * * @return <code>null</code> or a target part */ protected EditPart getTargetEditPart() { return targetEditPart; } /** * Lazily creates and returns the request used when communicating with the * target editpart. * * @return the target request */ protected Request getTargetRequest() { if (targetRequest == null) setTargetRequest(createTargetRequest()); return targetRequest; } /** * This method is called whenever an autoexpose occurs. When an autoexpose * occurs, it is possible that everything in the viewer has moved a little. * Therefore, by default, {@link AbstractTool#handleMove() handleMove()} is * called to simulate the mouse moving even though it didn't. */ protected void handleAutoexpose() { handleMove(); } /** * Called whenever the target editpart has changed. By default, the target * request is updated, and the new target is asked to show feedback. * Subclasses may extend this method if needed. * * @return <code>true</code> */ protected boolean handleEnteredEditPart() { updateTargetRequest(); showTargetFeedback(); return true; } /** * Called whenever the target editpart is about to change. By default, hover * is reset, in the case that a hover was showing something, and the target * being exited is asked to erase its feedback. * * @return <code>true</code> */ protected boolean handleExitingEditPart() { resetHover(); eraseTargetFeedback(); return true; } /** * Called from resetHover() iff hover is active. Subclasses may extend this * method to handle the hover stop event. Returns <code>true</code> if * something was done in response to the call. * * @see AbstractTool#isHoverActive() * @return <code>true</code> if the hover stop is processed in some way */ protected boolean handleHoverStop() { return false; } /** * Called when invalid input is encountered. By default, feedback is erased, * and the current command is set to the unexecutable command. The state * does not change, so the caller must set the state to * {@link AbstractTool#STATE_INVALID}. * * @return <code>true</code> */ protected boolean handleInvalidInput() { eraseTargetFeedback(); setCurrentCommand(UnexecutableCommand.INSTANCE); return true; } /** * An archaic method name that has been left here to force use of the new * name. * * @throws Exception * exc */ protected final void handleLeavingEditPart() throws Exception { } /** * Sets the target to <code>null</code>. * * @see org.eclipse.gef.tools.AbstractTool#handleViewerExited() */ protected boolean handleViewerExited() { setTargetEditPart(null); return true; } /** * Returns <code>true</code> if target feedback is being shown. * * @return <code>true</code> if showing target feedback */ protected boolean isShowingTargetFeedback() { return getFlag(FLAG_TARGET_FEEDBACK); } /** * Return <code>true</code> if the current target is locked. * * @see #lockTargetEditPart(EditPart) * @return <code>true</code> if the target is locked */ protected boolean isTargetLocked() { return getFlag(FLAG_LOCK_TARGET); } /** * Locks-in the given editpart as the target. Updating of the target will * not occur until {@link #unlockTargetEditPart()} is called. * * @param editpart * the target to be locked-in */ protected void lockTargetEditPart(EditPart editpart) { if (editpart == null) { unlockTargetEditPart(); return; } setFlag(FLAG_LOCK_TARGET, true); setTargetEditPart(editpart); } /** * Extended to reset the target lock flag. * * @see org.eclipse.gef.tools.AbstractTool#resetFlags() * @see #lockTargetEditPart(EditPart) */ protected void resetFlags() { setFlag(FLAG_LOCK_TARGET, false); super.resetFlags(); } /** * Resets hovering to inactive. * * @since 3.4 */ protected void resetHover() { if (isHoverActive()) handleHoverStop(); setHoverActive(false); } class QueuedAutoexpose implements Runnable { public void run() { if (exposeHelper != null) doAutoexpose(); } } /** * Sets the active autoexpose helper to the given helper, or * <code>null</code>. If the helper is not <code>null</code>, a runnable is * queued on the event thread that will trigger a subsequent * {@link #doAutoexpose()}. The helper is typically updated only on a hover * event. * * @param helper * the new autoexpose helper or <code>null</code> */ protected void setAutoexposeHelper(AutoexposeHelper helper) { exposeHelper = helper; if (exposeHelper == null) return; Display.getCurrent().asyncExec(new QueuedAutoexpose()); } /** * Sets the target editpart. If the target editpart is changing, this method * will call {@link #handleExitingEditPart()} for the previous target if not * <code>null</code>, and {@link #handleEnteredEditPart()} for the new * target, if not <code>null</code>. * * @param editpart * the new target */ protected void setTargetEditPart(EditPart editpart) { if (editpart != targetEditPart) { if (targetEditPart != null) handleExitingEditPart(); targetEditPart = editpart; if (getTargetRequest() instanceof TargetRequest) ((TargetRequest) getTargetRequest()) .setTargetEditPart(targetEditPart); handleEnteredEditPart(); } } /** * Sets the target request. This method is typically not called; subclasses * normally override {@link #createTargetRequest()}. * * @param req * the target request */ protected void setTargetRequest(Request req) { targetRequest = req; } /** * Asks the target editpart to show target feedback and sets the target * feedback flag. */ protected void showTargetFeedback() { if (getTargetEditPart() != null) getTargetEditPart().showTargetFeedback(getTargetRequest()); setFlag(FLAG_TARGET_FEEDBACK, true); } /** * Releases the targeting lock, and updates the target in case the mouse is * already over a new target. */ protected void unlockTargetEditPart() { setFlag(FLAG_LOCK_TARGET, false); updateTargetUnderMouse(); } /** * Updates the active {@link AutoexposeHelper}. Does nothing if there is * still an active helper. Otherwise, obtains a new helper (possible * <code>null</code>) at the current mouse location and calls * {@link #setAutoexposeHelper(AutoexposeHelper)}. */ protected void updateAutoexposeHelper() { if (exposeHelper != null) return; AutoexposeHelper.Search search; search = new AutoexposeHelper.Search(getLocation()); getCurrentViewer().findObjectAtExcluding(getLocation(), Collections.EMPTY_LIST, search); setAutoexposeHelper(search.result); } /** * Subclasses should override to update the target request. */ protected void updateTargetRequest() { } /** * Updates the target editpart and returns <code>true</code> if the target * changes. The target is updated by using the target conditional and the * target request. If the target has been locked, this method does nothing * and returns <code>false</code>. * * @return <code>true</code> if the target was changed */ protected boolean updateTargetUnderMouse() { if (!isTargetLocked()) { EditPart editPart = getCurrentViewer() .findObjectAtExcluding(getLocation(), getExclusionSet(), getTargetingConditional()); if (editPart != null) editPart = editPart.getTargetEditPart(getTargetRequest()); boolean changed = getTargetEditPart() != editPart; setTargetEditPart(editPart); return changed; } else return false; } /** * Returns <code>null</code> or the current autoexpose helper. * * @return null or a helper */ protected AutoexposeHelper getAutoexposeHelper() { return exposeHelper; } }