| /******************************************************************************* |
| * Copyright (c) 2000, 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.dnd; |
| |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.jface.util.SafeRunnable; |
| import org.eclipse.jface.util.TransferDropTargetListener; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DropTargetEvent; |
| import org.eclipse.swt.dnd.DropTargetListener; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.dnd.TransferData; |
| |
| /** |
| * A delegating drop adapter negotiates between a set of |
| * <code>TransferDropTargetListener</code> s On <code>dragEnter</code> the |
| * adapter determines the listener to be used for any further <code>drag*</code> |
| * callback. |
| */ |
| public class DelegatingDropAdapter implements DropTargetListener { |
| |
| private TransferDropTargetListener[] fListeners; |
| private TransferDropTargetListener fCurrentListener; |
| private int fOriginalDropType; |
| |
| /** |
| * Creates a new delegating drop adapter. |
| * |
| * @param listeners an array of potential listeners |
| */ |
| public DelegatingDropAdapter(TransferDropTargetListener[] listeners) { |
| fListeners= listeners; |
| } |
| |
| /** |
| * The cursor has entered the drop target boundaries. The current listener |
| * is updated, and <code>#dragEnter()</code> is forwarded to the current |
| * listener. |
| * |
| * @param event the drop target event |
| * @see DropTargetListener#dragEnter(DropTargetEvent) |
| */ |
| @Override |
| public void dragEnter(DropTargetEvent event) { |
| fOriginalDropType= event.detail; |
| updateCurrentListener(event); |
| } |
| |
| /** |
| * The cursor has left the drop target boundaries. The event is forwarded to |
| * the current listener. |
| * |
| * @param event the drop target event |
| * @see DropTargetListener#dragLeave(DropTargetEvent) |
| */ |
| @Override |
| public void dragLeave(final DropTargetEvent event) { |
| setCurrentListener(null, event); |
| } |
| |
| /** |
| * The operation being performed has changed (usually due to the user |
| * changing a drag modifier key while dragging). Updates the current |
| * listener and forwards this event to that listener. |
| * |
| * @param event the drop target event |
| * @see DropTargetListener#dragOperationChanged(DropTargetEvent) |
| */ |
| @Override |
| public void dragOperationChanged(final DropTargetEvent event) { |
| fOriginalDropType= event.detail; |
| TransferDropTargetListener oldListener= getCurrentListener(); |
| updateCurrentListener(event); |
| final TransferDropTargetListener newListener= getCurrentListener(); |
| // only notify the current listener if it hasn't changed based on the |
| // operation change. otherwise the new listener would get a dragEnter |
| // followed by a dragOperationChanged with the exact same event. |
| if (newListener != null && newListener == oldListener) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() throws Exception { |
| newListener.dragOperationChanged(event); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * The cursor is moving over the drop target. Updates the current listener |
| * and forwards this event to that listener. If no listener can handle the |
| * drag operation the <code>event.detail</code> field is set to |
| * <code>DND.DROP_NONE</code> to indicate an invalid drop. |
| * |
| * @param event the drop target event |
| * @see DropTargetListener#dragOver(DropTargetEvent) |
| */ |
| @Override |
| public void dragOver(final DropTargetEvent event) { |
| TransferDropTargetListener oldListener= getCurrentListener(); |
| updateCurrentListener(event); |
| final TransferDropTargetListener newListener= getCurrentListener(); |
| |
| // only notify the current listener if it hasn't changed based on the |
| // drag over. otherwise the new listener would get a dragEnter |
| // followed by a dragOver with the exact same event. |
| if (newListener != null && newListener == oldListener) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() throws Exception { |
| newListener.dragOver(event); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Forwards this event to the current listener, if there is one. Sets the |
| * current listener to <code>null</code> afterwards. |
| * |
| * @param event the drop target event |
| * @see DropTargetListener#drop(DropTargetEvent) |
| */ |
| @Override |
| public void drop(final DropTargetEvent event) { |
| updateCurrentListener(event); |
| if (getCurrentListener() != null) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() throws Exception { |
| getCurrentListener().drop(event); |
| } |
| }); |
| } |
| setCurrentListener(null, event); |
| } |
| |
| /** |
| * Forwards this event to the current listener if there is one. |
| * |
| * @param event the drop target event |
| * @see DropTargetListener#dropAccept(DropTargetEvent) |
| */ |
| @Override |
| public void dropAccept(final DropTargetEvent event) { |
| if (getCurrentListener() != null) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() throws Exception { |
| getCurrentListener().dropAccept(event); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Returns the listener which currently handles drop events. |
| * |
| * @return the <code>TransferDropTargetListener</code> which currently |
| * handles drop events. |
| */ |
| private TransferDropTargetListener getCurrentListener() { |
| return fCurrentListener; |
| } |
| |
| /** |
| * Returns the transfer data type supported by the given listener. Returns |
| * <code>null</code> if the listener does not support any of the specified |
| * data types. |
| * |
| * @param dataTypes available data types |
| * @param listener <code>TransferDropTargetListener</code> to use for |
| * testing supported data types. |
| * @return the transfer data type supported by the given listener or |
| * <code>null</code>. |
| */ |
| private TransferData getSupportedTransferType(TransferData[] dataTypes, TransferDropTargetListener listener) { |
| for (int i= 0; i < dataTypes.length; i++) { |
| if (listener.getTransfer().isSupportedType(dataTypes[i])) { |
| return dataTypes[i]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the combined set of <code>Transfer</code> types of all |
| * <code>TransferDropTargetListeners</code>. |
| * |
| * @return the combined set of <code>Transfer</code> types |
| */ |
| public Transfer[] getTransfers() { |
| Transfer[] types= new Transfer[fListeners.length]; |
| for (int i= 0; i < fListeners.length; i++) { |
| types[i]= fListeners[i].getTransfer(); |
| } |
| return types; |
| } |
| |
| /** |
| * Sets the current listener to <code>listener</code>. Sends the given |
| * <code>DropTargetEvent</code> if the current listener changes. |
| * |
| * @return <code>true</code> if the new listener is different than the |
| * previous <code>false</code> otherwise |
| */ |
| private boolean setCurrentListener(TransferDropTargetListener listener, final DropTargetEvent event) { |
| if (fCurrentListener == listener) |
| return false; |
| if (fCurrentListener != null) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() throws Exception { |
| fCurrentListener.dragLeave(event); |
| } |
| }); |
| } |
| fCurrentListener= listener; |
| if (fCurrentListener != null) { |
| SafeRunner.run(new SafeRunnable() { |
| @Override |
| public void run() throws Exception { |
| fCurrentListener.dragEnter(event); |
| } |
| }); |
| } |
| return true; |
| } |
| |
| /** |
| * Updates the current listener to one that can handle the drop. There can |
| * be many listeners and each listener may be able to handle many |
| * <code>TransferData</code> types. The first listener found that can |
| * handle a drop of one of the given <code>TransferData</code> types will |
| * be selected. If no listener can handle the drag operation the |
| * <code>event.detail</code> field is set to <code>DND.DROP_NONE</code> |
| * to indicate an invalid drop. |
| * |
| * @param event the drop target event |
| */ |
| private void updateCurrentListener(DropTargetEvent event) { |
| int originalDetail= event.detail; |
| // Revert the detail to the "original" drop type that the User |
| // indicated. This is necessary because the previous listener |
| // may have changed the detail to something other than what the |
| // user indicated. |
| event.detail= fOriginalDropType; |
| |
| for (int i= 0; i < fListeners.length; i++) { |
| TransferDropTargetListener listener= fListeners[i]; |
| TransferData dataType= getSupportedTransferType(event.dataTypes, listener); |
| if (dataType != null) { |
| TransferData originalDataType= event.currentDataType; |
| // set the data type supported by the drop listener |
| event.currentDataType= dataType; |
| if (listener.isEnabled(event)) { |
| // if the listener stays the same, set its previously |
| // determined |
| // event detail |
| if (!setCurrentListener(listener, event)) |
| event.detail= originalDetail; |
| return; |
| } else { |
| event.currentDataType= originalDataType; |
| } |
| } |
| } |
| setCurrentListener(null, event); |
| event.detail= DND.DROP_NONE; |
| } |
| } |