blob: f3dde14653c2e5241a5bd961497bebbd66b2a1c2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2009 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
* Stephan Wahlbrink <stephan.wahlbrink@walware.de> - Wrong operations mode/feedback for text drag over/drop in text editors - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206043
*******************************************************************************/
package org.eclipse.ui.internal;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dnd.IDragAndDropService;
import org.eclipse.ui.services.IDisposable;
/**
* Implementation for the <code>IDragAndDropService</code> to be used from
* <code>EditorSite</code>'s.
* </p><p>
* Adds a drop target to the given control that merges the site's
* drop behaviour with that specified by the <code>addMergedDropTarget</code> call.
* </p><p>
* The current implementation is only defined for EditorSite's and merges the
* given drop handling with the existing EditorSashContainer's behaviour.
* </p><p>
* NOTE: There is no cleanup (i.e. 'dispose') handling necessary for merged
* Drop Targets but the hooks are put into place to maintain the consistency
* of the implementation pattern.
* </p>
* @since 1.3
*
*/
public class EditorSiteDragAndDropServiceImpl implements IDragAndDropService, IDisposable {
// Key used to store/retrieve the MergedDropTarget instance from the real DropTarget
private static String MDT_KEY = "MDT"; //$NON-NLS-1$
/**
* Implementation of a DropTarget wrapper that will either delegate to the
* <code>primaryListener</code> if the event's <code>currentDataType</code>
* can be handled by it; otherwise the event is forwarded on to the
* listener specified by <code>secondaryListener</code>.
* </p><p>
* NOTE: we should perhaps refactor this out into an external class
* </p>
*
*/
private static class MergedDropTarget {
private DropTarget realDropTarget;
private Transfer[] secondaryTransfers;
private DropTargetListener secondaryListener;
private int secondaryOps;
private Transfer[] primaryTransfers;
private DropTargetListener primaryListener;
private int primaryOps;
public MergedDropTarget(Control control,
int priOps, Transfer[] priTransfers, DropTargetListener priListener,
int secOps, Transfer[] secTransfers, DropTargetListener secListener) {
realDropTarget = new DropTarget(control, priOps | secOps);
realDropTarget.setData(MDT_KEY, this);
// Cache the editor's transfers and listener
primaryTransfers = priTransfers;
primaryListener = priListener;
primaryOps = priOps;
// Cache the editor area's current transfers & listener
secondaryTransfers = secTransfers;
secondaryListener = secListener;
secondaryOps = secOps;
// Combine the two sets of transfers into one array
Transfer[] allTransfers = new Transfer[secondaryTransfers.length+primaryTransfers.length];
int curTransfer = 0;
for (int i = 0; i < primaryTransfers.length; i++) {
allTransfers[curTransfer++] = primaryTransfers[i];
}
for (int i = 0; i < secondaryTransfers.length; i++) {
allTransfers[curTransfer++] = secondaryTransfers[i];
}
realDropTarget.setTransfer(allTransfers);
// Create a listener that will delegate to the appropriate listener
// NOTE: the -editor- wins (i.e. it can over-ride WB behaviour if it wants
realDropTarget.addDropListener(new DropTargetListener() {
public void dragEnter(DropTargetEvent event) {
getAppropriateListener(event, true).dragEnter(event);
}
public void dragLeave(DropTargetEvent event) {
getAppropriateListener(event, false).dragLeave(event);
}
public void dragOperationChanged(DropTargetEvent event) {
getAppropriateListener(event, true).dragOperationChanged(event);
}
public void dragOver(DropTargetEvent event) {
getAppropriateListener(event, true).dragOver(event);
}
public void drop(DropTargetEvent event) {
getAppropriateListener(event, true).drop(event);
}
public void dropAccept(DropTargetEvent event) {
getAppropriateListener(event, true).dropAccept(event);
}
});
}
private DropTargetListener getAppropriateListener(DropTargetEvent event, boolean checkOperation) {
if (isSupportedType(primaryTransfers, event.currentDataType)) {
if (checkOperation && !isSupportedOperation(primaryOps, event.detail)) {
event.detail = DND.DROP_NONE;
}
return primaryListener;
}
if (checkOperation && !isSupportedOperation(secondaryOps, event.detail)) {
event.detail = DND.DROP_NONE;
}
return secondaryListener;
}
private boolean isSupportedType(Transfer[] transfers, TransferData transferType) {
for (int i = 0; i < transfers.length; i++) {
if (transfers[i].isSupportedType(transferType))
return true;
}
return false;
}
private boolean isSupportedOperation(int dropOps, int eventDetail) {
return ((dropOps | DND.DROP_DEFAULT) & eventDetail) != 0;
}
}
// Cache any listeners for cleanup
List addedListeners = new ArrayList();
/* (non-Javadoc)
* @see org.eclipse.ui.dnd.IEditorDropTargetService#addDropTarget(org.eclipse.swt.widgets.Control, int, org.eclipse.swt.dnd.Transfer[], org.eclipse.swt.dnd.DropTargetListener)
*/
public void addMergedDropTarget(Control control, int ops, Transfer[] transfers,
DropTargetListener listener) {
// First we have to remove any existing drop target from the control
removeMergedDropTarget(control);
// Capture the editor area's current ops, transfers & listener
int editorSiteOps = DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_LINK;
WorkbenchWindow ww = (WorkbenchWindow) PlatformUI.getWorkbench().getActiveWorkbenchWindow();
WorkbenchWindowConfigurer winConfigurer = ww.getWindowConfigurer();
Transfer[] editorSiteTransfers = winConfigurer.getTransfers();
DropTargetListener editorSiteListener = winConfigurer.getDropTargetListener();
// Create a new 'merged' drop Listener using combination of the desired
// transfers and the ones used by the EditorArea
MergedDropTarget newTarget = new MergedDropTarget(control, ops, transfers, listener,
editorSiteOps, editorSiteTransfers, editorSiteListener);
addedListeners.add(newTarget);
newTarget.realDropTarget.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
Object mdt = e.widget.getData(MDT_KEY);
addedListeners.remove(mdt);
}
});
}
/**
* This method will return the current drop target for the control
* (whether or not it was created using this service.
*
* @param control The control to get the drop target for
* @return The DropTarget for that control (could be null
*/
private DropTarget getCurrentDropTarget(Control control) {
if (control == null)
return null;
Object curDT = control.getData(DND.DROP_TARGET_KEY);
return (DropTarget)curDT;
}
/* (non-Javadoc)
* @see org.eclipse.ui.dnd.IDragAndDropService#removeMergedDropTarget(org.eclipse.swt.widgets.Control)
*/
public void removeMergedDropTarget(Control control) {
DropTarget targetForControl = getCurrentDropTarget(control);
if (targetForControl != null) {
targetForControl.dispose();
addedListeners.remove(targetForControl);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.services.IDisposable#dispose()
*/
public void dispose() {
addedListeners.clear();
}
}