| /******************************************************************************* |
| * Copyright (c) 2007, 2017 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.dltk.internal.ui.dnd; |
| |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DropTargetAdapter; |
| import org.eclipse.swt.dnd.DropTargetEvent; |
| import org.eclipse.swt.dnd.TransferData; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Item; |
| import org.eclipse.swt.widgets.TableItem; |
| import org.eclipse.swt.widgets.TreeItem; |
| |
| /** |
| * This adapter class provides generic drag-and-drop support for a viewer. |
| * <p> |
| * Subclasses must implement the following methods: |
| * <ul> |
| * <li><code>validateDrop</code> - identifies valid drop targets in viewer</li> |
| * <li><code>performDrop</code> - carries out a drop into a viewer</li> |
| * </ul> |
| * The <code>setFeedbackEnabled</code> method can be called to turn on and off |
| * visual insertion feedback (on by default). |
| * </p> |
| * <p> |
| * THIS IS (ALMOST) A ONE-TO-ONE COPY OF PLATFORMS ViewerDropAdapter REFERE |
| * THERE FIRST IF YOU NEED TO FIX SOMETHING. |
| * </p> |
| */ |
| public abstract class DLTKViewerDropAdapter extends DropTargetAdapter { |
| |
| /** |
| * Constant describing the position of the cursor relative to the target |
| * object. This means the mouse is positioned slightly before the target. |
| * |
| * @see #getCurrentLocation() |
| */ |
| public static final int LOCATION_BEFORE = 1; |
| |
| /** |
| * Constant describing the position of the cursor relative to the target |
| * object. This means the mouse is positioned slightly after the target. |
| * |
| * @see #getCurrentLocation() |
| */ |
| public static final int LOCATION_AFTER = 2; |
| |
| /** |
| * Constant describing the position of the cursor relative to the target |
| * object. This means the mouse is positioned directly on the target. |
| * |
| * @see #getCurrentLocation() |
| */ |
| public static final int LOCATION_ON = 3; |
| |
| /** |
| * Constant describing the position of the cursor relative to the target |
| * object. This means the mouse is not positioned over or near any valid |
| * target. |
| * |
| * @see #getCurrentLocation() |
| */ |
| public static final int LOCATION_NONE = 4; |
| |
| /** |
| * The viewer to which this drop support has been added. |
| */ |
| private Viewer viewer; |
| |
| /** |
| * The current operation. |
| */ |
| private int currentOperation = DND.DROP_NONE; |
| |
| /** |
| * The last valid operation. |
| */ |
| private int lastValidOperation = DND.DROP_NONE; |
| |
| /** |
| * The data item currently under the mouse. |
| */ |
| private Object currentTarget; |
| |
| /** |
| * Information about the position of the mouse relative to the target |
| * (before, on, or after the target. Location is one of the |
| * <code>LOCATION_* </code> constants defined in this type. |
| */ |
| private int currentLocation; |
| |
| /** |
| * A flag that allows adapter users to turn the insertion feedback on or |
| * off. Default is <code>true</code>. |
| */ |
| private boolean feedbackEnabled = true; |
| |
| /** |
| * A flag that allows adapter users to turn auto scrolling on or off. |
| * Default is <code>true</code>. |
| */ |
| private boolean scrollEnabled = true; |
| |
| /** |
| * A flag that allows adapter users to turn auto expanding on or off. |
| * Default is <code>true</code>. |
| */ |
| private boolean expandEnabled = true; |
| |
| /** |
| * A flag that allows adapter users to turn selection feedback on or off. |
| * Default is <code>true</code>. |
| */ |
| private boolean selectFeedbackEnabled = true; |
| |
| /** |
| * Creates a new drop adapter for the given viewer. |
| * |
| * @param viewer |
| * the viewer |
| */ |
| protected DLTKViewerDropAdapter(Viewer viewer) { |
| this.viewer = viewer; |
| } |
| |
| /** |
| * Returns the position of the given event's coordinates relative to its |
| * target. The position is determined to be before, after, or on the item, |
| * based on some threshold value. |
| * |
| * @param event |
| * the event |
| * @return one of the <code>LOCATION_* </code>constants defined in this |
| * class |
| */ |
| protected int determineLocation(DropTargetEvent event) { |
| if (!(event.item instanceof Item)) { |
| return DLTKViewerDropAdapter.LOCATION_NONE; |
| } |
| Item item = (Item) event.item; |
| Point coordinates = new Point(event.x, event.y); |
| coordinates = viewer.getControl().toControl(coordinates); |
| if (item != null) { |
| Rectangle bounds = getBounds(item); |
| if (bounds == null) { |
| return DLTKViewerDropAdapter.LOCATION_NONE; |
| } |
| if ((coordinates.y - bounds.y) < 5) { |
| return DLTKViewerDropAdapter.LOCATION_BEFORE; |
| } |
| if ((bounds.y + bounds.height - coordinates.y) < 5) { |
| return DLTKViewerDropAdapter.LOCATION_AFTER; |
| } |
| } |
| return DLTKViewerDropAdapter.LOCATION_ON; |
| } |
| |
| /** |
| * Returns the target item of the given drop event. |
| * |
| * @param event |
| * the event |
| * @return The target of the drop, may be <code>null</code>. |
| */ |
| protected Object determineTarget(DropTargetEvent event) { |
| return event.item == null ? null : event.item.getData(); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. The mouse has moved |
| * over the drop target. If the target item has changed, notify the action |
| * and check that it is still enabled. |
| */ |
| private void doDropValidation(DropTargetEvent event) { |
| currentOperation = determineOperation(currentTarget, lastValidOperation, |
| event.currentDataType, event.operations); |
| event.detail = currentOperation; |
| setFeedback(event, currentLocation); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. The drag has entered |
| * this widget's region. See if the drop should be allowed. |
| */ |
| @Override |
| public void dragEnter(DropTargetEvent event) { |
| currentTarget = determineTarget(event); |
| currentLocation = determineLocation(event); |
| lastValidOperation = event.detail; |
| doDropValidation(event); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. The drop operation |
| * has changed, see if the action should still be enabled. |
| */ |
| @Override |
| public void dragOperationChanged(DropTargetEvent event) { |
| currentTarget = determineTarget(event); |
| lastValidOperation = event.detail; |
| doDropValidation(event); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. The mouse has moved |
| * over the drop target. If the target item has changed, notify the action |
| * and check that it is still enabled. |
| */ |
| @Override |
| public void dragOver(DropTargetEvent event) { |
| // use newly revealed item as target if scrolling occurs |
| Object target = determineTarget(event); |
| |
| // set the location feedback |
| int oldLocation = currentLocation; |
| currentLocation = determineLocation(event); |
| setFeedback(event, currentLocation); |
| |
| // see if anything has really changed before doing validation. |
| if (target != currentTarget || currentLocation != oldLocation) { |
| currentTarget = target; |
| doDropValidation(event); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. The user has dropped |
| * something on the desktop viewer. |
| */ |
| @Override |
| public void drop(DropTargetEvent event) { |
| currentLocation = determineLocation(event); |
| |
| // perform the drop behavior |
| if (!performDrop(event.data)) { |
| event.detail = DND.DROP_NONE; |
| } |
| currentOperation = event.detail; |
| } |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. Last chance for the |
| * action to disable itself |
| */ |
| @Override |
| public void dropAccept(DropTargetEvent event) { |
| event.detail = determineOperation(currentTarget, event.detail, |
| event.currentDataType, event.operations); |
| } |
| |
| /** |
| * Returns the bounds of the given SWT tree or table item. |
| * |
| * @param item |
| * the SWT Item |
| * @return the bounds, or <code>null</code> if it is not a known type of |
| * item |
| */ |
| protected Rectangle getBounds(Item item) { |
| if (item instanceof TreeItem) { |
| return ((TreeItem) item).getBounds(); |
| } |
| if (item instanceof TableItem) { |
| return ((TableItem) item).getBounds(0); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a constant describing the position of the mouse relative to the |
| * target (before, on, or after the target. |
| * |
| * @return one of the <code>LOCATION_* </code> constants defined in this |
| * type |
| */ |
| protected int getCurrentLocation() { |
| return currentLocation; |
| } |
| |
| /** |
| * Returns the current operation. |
| * |
| * @return a <code>DROP_*</code> constant from class <code>DND</code> |
| * |
| * @see DND#DROP_COPY |
| * @see DND#DROP_MOVE |
| * @see DND#DROP_LINK |
| * @see DND#DROP_NONE |
| */ |
| protected int getCurrentOperation() { |
| return currentOperation; |
| } |
| |
| /** |
| * Returns the target object currently under the mouse. |
| * |
| * @return the current target object |
| */ |
| protected Object getCurrentTarget() { |
| return currentTarget; |
| } |
| |
| /** |
| * Returns whether visible insertion feedback should be presented to the |
| * user. |
| * <p> |
| * Typical insertion feedback is the horizontal insertion bars that appear |
| * between adjacent items while dragging. |
| * </p> |
| * |
| * @return <code>true</code> if visual feedback is desired, and |
| * <code>false</code> if not |
| */ |
| public boolean getFeedbackEnabled() { |
| return feedbackEnabled; |
| } |
| |
| /** |
| * Returns the object currently selected by the viewer. |
| * |
| * @return the selected object, or <code>null</code> if either no object or |
| * multiple objects are selected |
| */ |
| protected Object getSelectedObject() { |
| ISelection selection = viewer.getSelection(); |
| if (selection instanceof IStructuredSelection && !selection.isEmpty()) { |
| IStructuredSelection structured = (IStructuredSelection) selection; |
| return structured.getFirstElement(); |
| } |
| return null; |
| } |
| |
| /** |
| * @return the viewer to which this drop support has been added. |
| */ |
| protected Viewer getViewer() { |
| return viewer; |
| } |
| |
| /** |
| * Performs any work associated with the drop. |
| * <p> |
| * Subclasses must implement this method to provide drop behavior. |
| * </p> |
| * |
| * @param data |
| * the drop data |
| * @return <code>true</code> if the drop was successful, and |
| * <code>false</code> otherwise |
| */ |
| public abstract boolean performDrop(Object data); |
| |
| /* |
| * (non-Javadoc) Method declared on DropTargetAdapter. The mouse has moved |
| * over the drop target. If the target item has changed, notify the action |
| * and check that it is still enabled. |
| */ |
| private void setFeedback(DropTargetEvent event, int location) { |
| if (feedbackEnabled) { |
| switch (location) { |
| case LOCATION_BEFORE: |
| event.feedback = DND.FEEDBACK_INSERT_BEFORE; |
| break; |
| case LOCATION_AFTER: |
| event.feedback = DND.FEEDBACK_INSERT_AFTER; |
| break; |
| case LOCATION_ON: |
| default: |
| event.feedback = DND.FEEDBACK_SELECT; |
| break; |
| } |
| } |
| |
| // Explicitly inhibit SELECT feedback if desired |
| if (!selectFeedbackEnabled) { |
| event.feedback &= ~DND.FEEDBACK_SELECT; |
| } |
| |
| if (expandEnabled) { |
| event.feedback |= DND.FEEDBACK_EXPAND; |
| } |
| if (scrollEnabled) { |
| event.feedback |= DND.FEEDBACK_SCROLL; |
| } |
| } |
| |
| /** |
| * Sets whether visible insertion feedback should be presented to the user. |
| * <p> |
| * Typical insertion feedback is the horizontal insertion bars that appear |
| * between adjacent items while dragging. |
| * </p> |
| * |
| * @param value |
| * <code>true</code> if visual feedback is desired, and |
| * <code>false</code> if not |
| */ |
| public void setFeedbackEnabled(boolean value) { |
| feedbackEnabled = value; |
| } |
| |
| /** |
| * Sets whether selection feedback should be provided during dragging. |
| * |
| * @param value |
| * <code>true</code> if selection feedback is desired, and |
| * <code>false</code> if not |
| * |
| * @since 3.2 |
| */ |
| public void setSelectionFeedbackEnabled(boolean value) { |
| selectFeedbackEnabled = value; |
| } |
| |
| /** |
| * Sets whether auto scrolling and expanding should be provided during |
| * dragging. |
| * |
| * @param value |
| * <code>true</code> if scrolling and expanding is desired, and |
| * <code>false</code> if not |
| * @since 2.0 |
| */ |
| public void setScrollExpandEnabled(boolean value) { |
| expandEnabled = value; |
| scrollEnabled = value; |
| } |
| |
| /** |
| * Sets whether auto expanding should be provided during dragging. |
| * |
| * @param value |
| * <code>true</code> if expanding is desired, and |
| * <code>false</code> if not |
| * @since 3.4 |
| */ |
| public void setExpandEnabled(boolean value) { |
| expandEnabled = value; |
| } |
| |
| /** |
| * Sets whether auto scrolling should be provided during dragging. |
| * |
| * @param value |
| * <code>true</code> if scrolling is desired, and |
| * <code>false</code> if not |
| * @since 3.4 |
| */ |
| public void setScrollEnabled(boolean value) { |
| scrollEnabled = value; |
| } |
| |
| /** |
| * Validates dropping on the given object. This method is called whenever |
| * some aspect of the drop operation changes. |
| * <p> |
| * Subclasses must implement this method to define which drops make sense. |
| * </p> |
| * |
| * @param target |
| * the object that the mouse is currently hovering over, or |
| * <code>null</code> if the mouse is hovering over empty space |
| * @param operation |
| * the current drag operation (copy, move, etc.) |
| * @param transferType |
| * the current transfer type |
| * @return <code>true</code> if the drop is valid, and <code>false</code> |
| * otherwise |
| */ |
| public abstract boolean validateDrop(Object target, int operation, |
| TransferData transferType); |
| |
| /** |
| * Determine the operation which should be executed given the target and the |
| * operation requested by the user. This method is called whenever some |
| * aspect of the drop operation changes. The operation is one of |
| * DND#DROP_DEFAULT, DND#DROP_COPY, DND#DROP_MOVE, DND#DROP_LINK. |
| * <p> |
| * The method returns the operation valid in the given context. The result |
| * is one of DND#DROP_NONE, DND#DROP_COPY, DND#DROP_MOVE, DND#DROP_LINK, |
| * DND#DROP_DEFAULT. |
| * </p> |
| * <p> |
| * Subclasses can overwrite this method to define which operation does make |
| * sense in the given context. |
| * </p> |
| * |
| * @param target |
| * the object that the mouse is currently hovering over, or |
| * <code>null</code> if the mouse is hovering over empty space |
| * @param operation |
| * the current drag operation (copy, move, etc.) |
| * @param transferType |
| * the current transfer type |
| * @param operations |
| * a bitwise OR'ing of the operations that the DragSource can |
| * support |
| * @return the operation which will be executed if no modifier key is |
| * pressed by the user |
| * |
| * @see DND#DROP_NONE |
| * @see DND#DROP_MOVE |
| * @see DND#DROP_COPY |
| * @see DND#DROP_LINK |
| */ |
| protected int determineOperation(Object target, int operation, |
| TransferData transferType, int operations) { |
| if (!validateDrop(target, operation, transferType)) { |
| return DND.DROP_NONE; |
| } |
| |
| return operation; |
| } |
| } |