| /******************************************************************************* |
| * 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.draw2d.IFigure; |
| import org.eclipse.draw2d.PositionConstants; |
| import org.eclipse.draw2d.geometry.Dimension; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PrecisionRectangle; |
| import org.eclipse.draw2d.geometry.Rectangle; |
| |
| import org.eclipse.gef.EditPart; |
| import org.eclipse.gef.EditPartViewer; |
| import org.eclipse.gef.GraphicalEditPart; |
| import org.eclipse.gef.Request; |
| import org.eclipse.gef.SharedCursors; |
| import org.eclipse.gef.SnapToHelper; |
| import org.eclipse.gef.requests.CreateRequest; |
| import org.eclipse.gef.requests.CreationFactory; |
| |
| /** |
| * The CreationTool creates new {@link EditPart EditParts} via a |
| * {@link CreationFactory}. If the user simply clicks on the viewer, the default |
| * sized EditPart will be created at that point. If the user clicks and drags, |
| * the created EditPart will be sized based on where the user clicked and |
| * dragged. |
| */ |
| public class CreationTool extends TargetingTool { |
| |
| /** |
| * Property to be used in {@link AbstractTool#setProperties(java.util.Map)} |
| * for {@link #setFactory(CreationFactory)}. |
| */ |
| public static final Object PROPERTY_CREATION_FACTORY = "factory"; //$NON-NLS-1$ |
| |
| private CreationFactory factory; |
| private SnapToHelper helper; |
| |
| /** |
| * Default constructor. Sets the default and disabled cursors. |
| */ |
| public CreationTool() { |
| setDefaultCursor(SharedCursors.CURSOR_TREE_ADD); |
| setDisabledCursor(SharedCursors.NO); |
| } |
| |
| /** |
| * Constructs a new CreationTool with the given factory. |
| * |
| * @param aFactory |
| * the creation factory |
| */ |
| public CreationTool(CreationFactory aFactory) { |
| this(); |
| setFactory(aFactory); |
| } |
| |
| /** |
| * @see org.eclipse.gef.tools.AbstractTool#applyProperty(java.lang.Object, |
| * java.lang.Object) |
| */ |
| protected void applyProperty(Object key, Object value) { |
| if (PROPERTY_CREATION_FACTORY.equals(key)) { |
| if (value instanceof CreationFactory) |
| setFactory((CreationFactory) value); |
| return; |
| } |
| super.applyProperty(key, value); |
| } |
| |
| /** |
| * @see org.eclipse.gef.tools.AbstractTool#calculateCursor() |
| */ |
| protected Cursor calculateCursor() { |
| /* |
| * Fix for Bug# 66010 The following two lines of code were added for the |
| * case where a tool is activated via the keyboard (that code hasn't |
| * been released yet). However, they were causing a problem as described |
| * in 66010. Since the keyboard activation code is not being released |
| * for 3.0, the following lines are being commented out. |
| */ |
| // if (isInState(STATE_INITIAL)) |
| // return getDefaultCursor(); |
| return super.calculateCursor(); |
| } |
| |
| /** |
| * Creates a {@link CreateRequest} and sets this tool's factory on the |
| * request. |
| * |
| * @see org.eclipse.gef.tools.TargetingTool#createTargetRequest() |
| */ |
| protected Request createTargetRequest() { |
| CreateRequest request = new CreateRequest(); |
| request.setFactory(getFactory()); |
| return request; |
| } |
| |
| /** |
| * @see org.eclipse.gef.Tool#deactivate() |
| */ |
| public void deactivate() { |
| super.deactivate(); |
| helper = null; |
| } |
| |
| /** |
| * @see org.eclipse.gef.tools.AbstractTool#getCommandName() |
| */ |
| protected String getCommandName() { |
| return REQ_CREATE; |
| } |
| |
| /** |
| * Cast the target request to a CreateRequest and returns it. |
| * |
| * @return the target request as a CreateRequest |
| * @see TargetingTool#getTargetRequest() |
| */ |
| protected CreateRequest getCreateRequest() { |
| return (CreateRequest) getTargetRequest(); |
| } |
| |
| /** |
| * @see org.eclipse.gef.tools.AbstractTool#getDebugName() |
| */ |
| protected String getDebugName() { |
| return "Creation Tool";//$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns the creation factory used to create the new EditParts. |
| * |
| * @return the creation factory |
| */ |
| protected CreationFactory getFactory() { |
| return factory; |
| } |
| |
| /** |
| * The creation tool only works by clicking mouse button 1 (the left mouse |
| * button in a right-handed world). If any other button is pressed, the tool |
| * goes into an invalid state. Otherwise, it goes into the drag state, |
| * updates the request's location and calls |
| * {@link TargetingTool#lockTargetEditPart(EditPart)} with the edit part |
| * that was just clicked on. |
| * |
| * @see org.eclipse.gef.tools.AbstractTool#handleButtonDown(int) |
| */ |
| protected boolean handleButtonDown(int button) { |
| if (button != 1) { |
| setState(STATE_INVALID); |
| handleInvalidInput(); |
| return true; |
| } |
| if (stateTransition(STATE_INITIAL, STATE_DRAG)) { |
| getCreateRequest().setLocation(getLocation()); |
| lockTargetEditPart(getTargetEditPart()); |
| // Snap only when size on drop is employed |
| if (getTargetEditPart() != null) |
| helper = (SnapToHelper) getTargetEditPart().getAdapter( |
| SnapToHelper.class); |
| } |
| return true; |
| } |
| |
| /** |
| * If the tool is currently in a drag or drag-in-progress state, it goes |
| * into the terminal state, performs some cleanup (erasing feedback, |
| * unlocking target edit part), and then calls {@link #performCreation(int)} |
| * . |
| * |
| * @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int) |
| */ |
| protected boolean handleButtonUp(int button) { |
| if (stateTransition(STATE_DRAG | STATE_DRAG_IN_PROGRESS, STATE_TERMINAL)) { |
| // HACK for DND in RAP |
| handleMove(); |
| eraseTargetFeedback(); |
| unlockTargetEditPart(); |
| performCreation(button); |
| } |
| |
| setState(STATE_TERMINAL); |
| handleFinished(); |
| |
| return true; |
| } |
| |
| /** |
| * Updates the request, sets the current command, and asks to show feedback. |
| * |
| * @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress() |
| */ |
| protected boolean handleDragInProgress() { |
| if (isInState(STATE_DRAG_IN_PROGRESS)) { |
| updateTargetRequest(); |
| setCurrentCommand(getCommand()); |
| showTargetFeedback(); |
| } |
| return true; |
| } |
| |
| /** |
| * @see org.eclipse.gef.tools.AbstractTool#handleDragStarted() |
| */ |
| protected boolean handleDragStarted() { |
| return stateTransition(STATE_DRAG, STATE_DRAG_IN_PROGRESS); |
| } |
| |
| /** |
| * If the user is in the middle of creating a new edit part, the tool erases |
| * feedback and goes into the invalid state when focus is lost. |
| * |
| * @see org.eclipse.gef.tools.AbstractTool#handleFocusLost() |
| */ |
| protected boolean handleFocusLost() { |
| if (isInState(STATE_DRAG | STATE_DRAG_IN_PROGRESS)) { |
| eraseTargetFeedback(); |
| setState(STATE_INVALID); |
| handleFinished(); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @see org.eclipse.gef.tools.TargetingTool#handleHover() |
| */ |
| protected boolean handleHover() { |
| if (isInState(STATE_INITIAL)) |
| updateAutoexposeHelper(); |
| return true; |
| } |
| |
| /** |
| * Updates the request and mouse target, gets the current command and asks |
| * to show feedback. |
| * |
| * @see org.eclipse.gef.tools.AbstractTool#handleMove() |
| */ |
| protected boolean handleMove() { |
| updateTargetRequest(); |
| updateTargetUnderMouse(); |
| setCurrentCommand(getCommand()); |
| showTargetFeedback(); |
| return true; |
| } |
| |
| /** |
| * Executes the current command and selects the newly created object. The |
| * button that was released to cause this creation is passed in, but since |
| * {@link #handleButtonDown(int)} goes into the invalid state if the button |
| * pressed is not button 1, this will always be button 1. |
| * |
| * @param button |
| * the button that was pressed |
| */ |
| protected void performCreation(int button) { |
| EditPartViewer viewer = getCurrentViewer(); |
| executeCurrentCommand(); |
| selectAddedObject(viewer); |
| } |
| |
| /* |
| * Add the newly created object to the viewer's selected objects. |
| */ |
| private void selectAddedObject(EditPartViewer viewer) { |
| final Object model = getCreateRequest().getNewObject(); |
| if (model == null || viewer == null) |
| return; |
| Object editpart = viewer.getEditPartRegistry().get(model); |
| if (editpart instanceof EditPart) { |
| // Force the new object to get positioned in the viewer. |
| viewer.flush(); |
| viewer.select((EditPart) editpart); |
| } |
| } |
| |
| /** |
| * Sets the creation factory used to create the new edit parts. |
| * |
| * @param factory |
| * the factory |
| */ |
| public void setFactory(CreationFactory factory) { |
| this.factory = factory; |
| } |
| |
| /** |
| * Sets the location (and size if the user is performing size-on-drop) of |
| * the request. |
| * |
| * @see org.eclipse.gef.tools.TargetingTool#updateTargetRequest() |
| */ |
| protected void updateTargetRequest() { |
| CreateRequest createRequest = getCreateRequest(); |
| if (isInState(STATE_DRAG_IN_PROGRESS)) { |
| Point loq = getStartLocation(); |
| Rectangle bounds = new Rectangle(loq, loq); |
| bounds.union(loq.getTranslated(getDragMoveDelta())); |
| createRequest.setSize(bounds.getSize()); |
| createRequest.setLocation(bounds.getLocation()); |
| createRequest.getExtendedData().clear(); |
| createRequest.setSnapToEnabled(!getCurrentInput().isModKeyDown( |
| MODIFIER_NO_SNAPPING)); |
| if (helper != null && createRequest.isSnapToEnabled()) { |
| PrecisionRectangle baseRect = new PrecisionRectangle(bounds); |
| PrecisionRectangle result = baseRect.getPreciseCopy(); |
| helper.snapRectangle(createRequest, PositionConstants.NSEW, |
| baseRect, result); |
| createRequest.setLocation(result.getLocation()); |
| createRequest.setSize(result.getSize()); |
| } |
| enforceConstraintsForSizeOnDropCreate(createRequest); |
| } else { |
| createRequest.setSize(null); |
| createRequest.setLocation(getLocation()); |
| createRequest.setSnapToEnabled(false); |
| } |
| } |
| |
| /** |
| * Ensures size constraints (by default minimum and maximum) are respected |
| * by the given request. May be overwritten by clients to enforce additional |
| * constraints. |
| * |
| * @since 3.7 |
| */ |
| protected void enforceConstraintsForSizeOnDropCreate(CreateRequest request) { |
| CreateRequest createRequest = (CreateRequest) getTargetRequest(); |
| if (createRequest.getSize() != null) { |
| // ensure create request respects minimum and maximum size |
| // constraints |
| PrecisionRectangle constraint = new PrecisionRectangle( |
| createRequest.getLocation(), createRequest.getSize()); |
| ((GraphicalEditPart) getTargetEditPart()).getContentPane() |
| .translateToRelative(constraint); |
| constraint.setSize(Dimension.max(constraint.getSize(), |
| getMinimumSizeFor(createRequest))); |
| constraint.setSize(Dimension.min(constraint.getSize(), |
| getMaximumSizeFor(createRequest))); |
| ((GraphicalEditPart) getTargetEditPart()).getContentPane() |
| .translateToRelative(constraint); |
| createRequest.setSize(constraint.getSize()); |
| } |
| } |
| |
| /** |
| * Determines the <em>maximum</em> size for CreateRequest's size on drop. It |
| * is called from {@link #enforceConstraintsForSizeOnDropCreate(CreateRequest)} |
| * during creation. By default, a large <code>Dimension</code> is returned. |
| * |
| * @param request |
| * the request. |
| * @return the minimum size |
| * @since 3.7 |
| */ |
| protected Dimension getMaximumSizeFor(CreateRequest request) { |
| return IFigure.MAX_DIMENSION; |
| } |
| |
| /** |
| * Determines the <em>minimum</em> size for CreateRequest's size on drop. It |
| * is called from {@link #enforceConstraintsForSizeOnDropCreate(CreateRequest)} |
| * during creation. By default, a small <code>Dimension</code> is returned. |
| * |
| * @param request |
| * the request. |
| * @return the minimum size |
| * @since 3.7 |
| */ |
| protected Dimension getMinimumSizeFor(CreateRequest request) { |
| return IFigure.MIN_DIMENSION; |
| } |
| |
| } |