blob: 601542f924acd0c831f30bf1dfa6d5029de58c37 [file] [log] [blame]
/*******************************************************************************
* 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.jface.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
/**
* A <code>DelegatingDragAdapter</code> is a <code>DragSourceListener</code> that
* maintains and delegates to a set of {@link TransferDragSourceListener}s. Each
* TransferDragSourceListener can then be implemented as if it were the
* <code>DragSource's</code> only DragSourceListener.
* <p>
* When a drag is started, a subset of all <code>TransferDragSourceListeners</code>
* is generated and stored in a list of <i>active</i> listeners. This subset is
* calculated by forwarding {@link DragSourceListener#dragStart(DragSourceEvent)} to
* every listener, and checking if the {@link DragSourceEvent#doit doit} field is left
* set to <code>true</code>.
* </p>
* The <code>DragSource</code>'s set of supported Transfer types ({@link
* DragSource#setTransfer(Transfer[])}) is updated to reflect the Transfer types
* corresponding to the active listener subset.
* <p>
* If and when {@link #dragSetData(DragSourceEvent)} is called, a single
* <code>TransferDragSourceListener</code> is chosen, and only it is allowed to set the
* drag data. The chosen listener is the first listener in the subset of active listeners
* whose Transfer supports ({@link Transfer#isSupportedType(TransferData)}) the
* <code>dataType</code> in the <code>DragSourceEvent</code>.
* </p>
* <p>
* The following example snippet shows a <code>DelegatingDragAdapter</code> with two
* <code>TransferDragSourceListeners</code>. One implements drag of text strings,
* the other supports file transfer and demonstrates how a listener can be disabled using
* the dragStart method.
* </p>
* <code><pre>
* final TreeViewer viewer = new TreeViewer(shell, SWT.NONE);
*
* DelegatingDragAdapter dragAdapter = new DelegatingDragAdapter();
* dragAdapter.addDragSourceListener(new TransferDragSourceListener() {
* public Transfer getTransfer() {
* return TextTransfer.getInstance();
* }
* public void dragStart(DragSourceEvent event) {
* // always enabled, can control enablement based on selection etc.
* }
* public void dragSetData(DragSourceEvent event) {
* event.data = "Transfer data";
* }
* public void dragFinished(DragSourceEvent event) {
* // no clean-up required
* }
* });
* dragAdapter.addDragSourceListener(new TransferDragSourceListener() {
* public Transfer getTransfer() {
* return FileTransfer.getInstance();
* }
* public void dragStart(DragSourceEvent event) {
* // enable drag listener if there is a viewer selection
* event.doit = !viewer.getSelection().isEmpty();
* }
* public void dragSetData(DragSourceEvent event) {
* File file1 = new File("C:/temp/file1");
* File file2 = new File("C:/temp/file2");
* event.data = new String[] {file1.getAbsolutePath(), file2.getAbsolutePath()};
* }
* public void dragFinished(DragSourceEvent event) {
* // no clean-up required
* }
* });
* viewer.addDragSupport(DND.DROP_COPY | DND.DROP_MOVE, dragAdapter.getTransfers(), dragAdapter);
* </pre></code>
* @since 3.0
*/
public class DelegatingDragAdapter implements DragSourceListener {
private List listeners = new ArrayList();
private List activeListeners = new ArrayList();
private TransferDragSourceListener currentListener;
/**
* Adds the given <code>TransferDragSourceListener</code>.
*
* @param listener the new listener
*/
public void addDragSourceListener(TransferDragSourceListener listener) {
listeners.add(listener);
}
/**
* The drop has successfully completed. This event is forwarded to the current
* drag listener.
* Doesn't update the current listener, since the current listener is already the one
* that completed the drag operation.
*
* @param event the drag source event
* @see DragSourceListener#dragFinished(DragSourceEvent)
*/
public void dragFinished(final DragSourceEvent event) {
// if (Policy.DEBUG_DRAG_DROP)
// System.out.println("Drag Finished: " + toString()); //$NON-NLS-1$
SafeRunnable.run(new SafeRunnable() {
public void run() throws Exception {
if (currentListener != null) {
// there is a listener that can handle the drop, delegate the event
currentListener.dragFinished(event);
} else {
// The drag was canceled and currentListener was never set, so send the
// dragFinished event to all the active listeners.
Iterator iterator = activeListeners.iterator();
while (iterator.hasNext())
((TransferDragSourceListener) iterator.next())
.dragFinished(event);
}
}
});
currentListener = null;
activeListeners.clear();
}
/**
* The drop data is requested.
* Updates the current listener and then forwards the event to it.
*
* @param event the drag source event
* @see DragSourceListener#dragSetData(DragSourceEvent)
*/
public void dragSetData(final DragSourceEvent event) {
// if (Policy.DEBUG_DRAG_DROP)
// System.out.println("Drag Set Data: " + toString()); //$NON-NLS-1$
updateCurrentListener(event); // find a listener that can provide the given data type
if (currentListener != null) {
SafeRunnable.run(new SafeRunnable() {
public void run() throws Exception {
currentListener.dragSetData(event);
}
});
}
}
/**
* A drag operation has started.
* Forwards this event to each listener. A listener must set <code>event.doit</code>
* to <code>false</code> if it cannot handle the drag operation. If a listener can
* handle the drag, it is added to the list of active listeners.
* The drag is aborted if there are no listeners that can handle it.
*
* @param event the drag source event
* @see DragSourceListener#dragStart(DragSourceEvent)
*/
public void dragStart(final DragSourceEvent event) {
// if (Policy.DEBUG_DRAG_DROP)
// System.out.println("Drag Start: " + toString()); //$NON-NLS-1$
boolean doit = false; // true if any one of the listeners can handle the drag
List transfers = new ArrayList(listeners.size());
activeListeners.clear();
for (int i = 0; i < listeners.size(); i++) {
final TransferDragSourceListener listener = (TransferDragSourceListener) listeners
.get(i);
event.doit = true; // restore event.doit
SafeRunnable.run(new SafeRunnable() {
public void run() throws Exception {
listener.dragStart(event);
}
});
if (event.doit) { // the listener can handle this drag
transfers.add(listener.getTransfer());
activeListeners.add(listener);
}
doit |= event.doit;
}
if (doit)
((DragSource) event.widget).setTransfer((Transfer[]) transfers
.toArray(new Transfer[transfers.size()]));
event.doit = doit;
}
/**
* Returns the <code>Transfer<code>s from every <code>TransferDragSourceListener</code>.
*
* @return the combined <code>Transfer</code>s
*/
public Transfer[] getTransfers() {
Transfer[] types = new Transfer[listeners.size()];
for (int i = 0; i < listeners.size(); i++) {
TransferDragSourceListener listener = (TransferDragSourceListener) listeners
.get(i);
types[i] = listener.getTransfer();
}
return types;
}
/**
* Returns <code>true</code> if there are no listeners to delegate drag events to.
*
* @return <code>true</code> if there are no <code>TransferDragSourceListeners</code>
* <code>false</code> otherwise.
*/
public boolean isEmpty() {
return listeners.isEmpty();
}
/**
* Removes the given <code>TransferDragSourceListener</code>.
* Listeners should not be removed while a drag and drop operation is in progress.
*
* @param listener the <code>TransferDragSourceListener</code> to remove
*/
public void removeDragSourceListener(TransferDragSourceListener listener) {
listeners.remove(listener);
if (currentListener == listener)
currentListener = null;
if (activeListeners.contains(listener))
activeListeners.remove(listener);
}
/**
* Updates the current listener to one that can handle the drag. There can
* be many listeners and each listener may be able to handle many <code>TransferData</code>
* types. The first listener found that supports one of the <code>TransferData</ode>
* types specified in the <code>DragSourceEvent</code> will be selected.
*
* @param event the drag source event
*/
private void updateCurrentListener(DragSourceEvent event) {
currentListener = null;
if (event.dataType == null)
return;
Iterator iterator = activeListeners.iterator();
while (iterator.hasNext()) {
TransferDragSourceListener listener = (TransferDragSourceListener) iterator
.next();
if (listener.getTransfer().isSupportedType(event.dataType)) {
// if (Policy.DEBUG_DRAG_DROP)
// System.out.println("Current drag listener: " + listener); //$NON-NLS-1$
currentListener = listener;
return;
}
}
}
}