/*******************************************************************************
* Copyright (c) 2006, 2010 Wind River Systems, Inc. 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:
* Anton Leherbauer (Wind River Systems) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.navigator;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.actions.CopyFilesAndFoldersOperation;
import org.eclipse.ui.actions.MoveFilesAndFoldersOperation;
import org.eclipse.ui.actions.ReadOnlyStateChecker;
import org.eclipse.ui.ide.dialogs.ImportTypeDialog;
import org.eclipse.ui.navigator.CommonDropAdapter;
import org.eclipse.ui.navigator.CommonDropAdapterAssistant;
import org.eclipse.ui.part.ResourceTransfer;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.ui.cview.CViewMessages;
import org.eclipse.cdt.internal.ui.util.ExceptionHandler;
/**
* A Common Navigator drop adapter assistant handling dropping of <code>ICElement</code>s.
*
* @see org.eclipse.cdt.internal.ui.cview.SelectionTransferDropAdapter
*/
public class CNavigatorDropAdapterAssistant extends CommonDropAdapterAssistant {
private static final IResource[] NO_RESOURCES = new IResource[0];
/*
* @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#isSupportedType(org.eclipse.swt.dnd.TransferData)
*/
@Override
public boolean isSupportedType(TransferData transferType) {
return super.isSupportedType(transferType)
|| ResourceTransfer.getInstance().isSupportedType(transferType)
|| FileTransfer.getInstance().isSupportedType(transferType);
}
/*
* @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#handleDrop(org.eclipse.ui.navigator.CommonDropAdapter, org.eclipse.swt.dnd.DropTargetEvent, java.lang.Object)
*/
@Override
public IStatus handleDrop(CommonDropAdapter dropAdapter,
DropTargetEvent event, Object target) {
try {
// drop in folder
if (target instanceof ICContainer ||
target instanceof ICProject ||
target instanceof IContainer ||
(dropAdapter.getCurrentOperation() == DND.DROP_COPY && (
target instanceof IFile ||
target instanceof ITranslationUnit))) {
final Object data= event.data;
if (data == null) {
return Status.CANCEL_STATUS;
}
final IContainer destination= getDestination(target);
if (destination == null) {
return Status.CANCEL_STATUS;
}
IResource[] resources = null;
TransferData currentTransfer = dropAdapter.getCurrentTransfer();
final int dropOperation = dropAdapter.getCurrentOperation();
if (LocalSelectionTransfer.getTransfer().isSupportedType(
currentTransfer)) {
resources = getSelectedResources();
if (target instanceof ITranslationUnit) {
if (handleDropCopy(target, event).isOK()) {
// drop inside translation unit - we are done
return Status.OK_STATUS;
}
}
} else if (ResourceTransfer.getInstance().isSupportedType(
currentTransfer)) {
resources = (IResource[]) event.data;
}
if (FileTransfer.getInstance().isSupportedType(currentTransfer)) {
final String[] names = (String[]) data;
// Run the import operation asynchronously.
// Otherwise the drag source (e.g., Windows Explorer) will be blocked
// while the operation executes. Fixes bug 35796.
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
getShell().forceActive();
CopyFilesAndFoldersOperation op= new CopyFilesAndFoldersOperation(getShell());
op.copyOrLinkFiles(names, destination, dropOperation);
}
});
} else if (event.detail == DND.DROP_COPY || event.detail == DND.DROP_LINK) {
return performResourceCopy(dropAdapter, getShell(), resources);
} else {
ReadOnlyStateChecker checker = new ReadOnlyStateChecker(
getShell(),
"Move Resource Action", //$NON-NLS-1$
"Move Resource Action");//$NON-NLS-1$
resources = checker.checkReadOnlyResources(resources);
MoveFilesAndFoldersOperation operation = new MoveFilesAndFoldersOperation(getShell());
operation.copyResources(resources, destination);
}
return Status.OK_STATUS;
}
switch(event.detail) {
case DND.DROP_MOVE:
return handleDropMove(target, event);
case DND.DROP_COPY:
return handleDropCopy(target, event);
}
} catch (CModelException e){
ExceptionHandler.handle(e, CViewMessages.SelectionTransferDropAdapter_error_title, CViewMessages.SelectionTransferDropAdapter_error_message);
return e.getStatus();
} catch(InvocationTargetException e) {
ExceptionHandler.handle(e, CViewMessages.SelectionTransferDropAdapter_error_title, CViewMessages.SelectionTransferDropAdapter_error_exception);
return Status.CANCEL_STATUS;
} catch (InterruptedException e) {
//ok
} finally {
// The drag source listener must not perform any operation
// since this drop adapter did the remove of the source even
// if we moved something.
event.detail= DND.DROP_NONE;
}
return Status.CANCEL_STATUS;
}
/*
* @see org.eclipse.ui.navigator.CommonDropAdapterAssistant#validateDrop(java.lang.Object, int, org.eclipse.swt.dnd.TransferData)
*/
@Override
public IStatus validateDrop(Object target, int operation,
TransferData transferType) {
// drop in folder
if (target instanceof ICContainer ||
target instanceof ICProject ||
target instanceof IContainer ||
(operation == DND.DROP_COPY && (
target instanceof IFile ||
target instanceof ITranslationUnit))) {
IContainer destination= getDestination(target);
if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) {
IResource[] selectedResources= getSelectedResources();
if (selectedResources.length > 0) {
for (IResource res : selectedResources) {
if(res instanceof IProject) {
// drop of projects not supported on other IResources
// "Path for project must have only one segment."
return Status.CANCEL_STATUS;
}
}
if (operation == DND.DROP_COPY || operation == DND.DROP_LINK) {
CopyFilesAndFoldersOperation op = new CopyFilesAndFoldersOperation(getShell());
if (op.validateDestination(destination, selectedResources) == null) {
return Status.OK_STATUS;
}
} else {
MoveFilesAndFoldersOperation op = new MoveFilesAndFoldersOperation(getShell());
if (op.validateDestination(destination, selectedResources) == null) {
return Status.OK_STATUS;
}
}
}
} else if (FileTransfer.getInstance().isSupportedType(transferType)) {
String[] sourceNames = (String[]) FileTransfer.getInstance().nativeToJava(transferType);
if (sourceNames == null) {
// source names will be null on Linux. Use empty names to do
// destination validation.
// Fixes bug 29778
sourceNames = new String[0];
}
CopyFilesAndFoldersOperation copyOperation = new CopyFilesAndFoldersOperation(
getShell());
if (null != copyOperation.validateImportDestination(destination,
sourceNames)) {
return Status.CANCEL_STATUS;
}
return Status.OK_STATUS;
}
}
if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) {
try {
switch(operation) {
case DND.DROP_DEFAULT:
return handleValidateMove(target);
case DND.DROP_COPY:
return handleValidateCopy(target);
case DND.DROP_MOVE:
return handleValidateMove(target);
}
} catch (CModelException e){
ExceptionHandler.handle(e, CViewMessages.SelectionTransferDropAdapter_error_title, CViewMessages.SelectionTransferDropAdapter_error_message);
}
}
return Status.CANCEL_STATUS;
}
private IStatus handleValidateCopy(Object target) throws CModelException{
if (target != null) {
ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
ICElement[] cElements= getCElements(selection);
if (cElements == null || cElements.length == 0) {
return Status.CANCEL_STATUS;
}
if (!canCopyElements(cElements))
return Status.CANCEL_STATUS;
if (target instanceof ISourceReference) {
for (ICElement cElement : cElements) {
if (cElement.getElementType() <= ICElement.C_UNIT) {
return Status.CANCEL_STATUS;
}
}
return Status.OK_STATUS;
}
}
return Status.CANCEL_STATUS;
}
private IStatus handleValidateMove(Object target) throws CModelException {
if (target != null) {
ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
ICElement[] cElements= getCElements(selection);
if (cElements == null || cElements.length == 0) {
return Status.CANCEL_STATUS;
}
if (Arrays.asList(cElements).contains(target)) {
return Status.CANCEL_STATUS;
}
if (!canMoveElements(cElements)) {
return Status.CANCEL_STATUS;
}
if (target instanceof ISourceReference) {
for (ICElement cElement : cElements) {
if (cElement.getElementType() <= ICElement.C_UNIT) {
return Status.CANCEL_STATUS;
}
}
return Status.OK_STATUS;
}
}
return Status.CANCEL_STATUS;
}
/**
* Performs a resource copy.
* Cloned from ResourceDropAdapterAssistant to support linked resources (bug 319405).
*/
private IStatus performResourceCopy(CommonDropAdapter dropAdapter,
Shell shell, IResource[] sources) {
IContainer target = getDestination(dropAdapter.getCurrentTarget());
if (target == null) {
return Status.CANCEL_STATUS;
}
boolean shouldLinkAutomatically = false;
if (target.isVirtual()) {
shouldLinkAutomatically = true;
for (int i = 0; i < sources.length; i++) {
if ((sources[i].getType() != IResource.FILE) && (sources[i].getLocation() != null)) {
// If the source is a folder, but the location is null (a
// broken link, for example),
// we still generate a link automatically (the best option).
shouldLinkAutomatically = false;
break;
}
}
}
CopyFilesAndFoldersOperation operation = new CopyFilesAndFoldersOperation(shell);
// if the target is a virtual folder and all sources are files, then
// automatically create links
if (shouldLinkAutomatically) {
operation.setCreateLinks(true);
operation.copyResources(sources, target);
} else {
boolean allSourceAreLinksOrVirtualFolders = true;
for (int i = 0; i < sources.length; i++) {
if (!sources[i].isVirtual() && !sources[i].isLinked()) {
allSourceAreLinksOrVirtualFolders = false;
break;
}
}
// if all sources are either links or groups, copy then normally,
// don't show the dialog
if (!allSourceAreLinksOrVirtualFolders) {
ImportTypeDialog dialog = new ImportTypeDialog(getShell(), dropAdapter.getCurrentOperation(), sources, target);
dialog.setResource(target);
if (dialog.open() == Window.OK) {
if (dialog.getSelection() == ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS)
operation.setVirtualFolders(true);
if (dialog.getSelection() == ImportTypeDialog.IMPORT_LINK)
operation.setCreateLinks(true);
if (dialog.getVariable() != null)
operation.setRelativeVariable(dialog.getVariable());
operation.copyResources(sources, target);
} else
return Status.CANCEL_STATUS;
} else
operation.copyResources(sources, target);
}
return Status.OK_STATUS;
}
private IStatus handleDropCopy(final Object target, DropTargetEvent event) throws CModelException, InvocationTargetException, InterruptedException{
ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
final ICElement[] cElements= getCElements(selection);
if (target instanceof ICElement && cElements.length > 0) {
ICElement cTarget = (ICElement)target;
ICElement parent = cTarget;
boolean isTargetTranslationUnit = cTarget instanceof ITranslationUnit;
if (!isTargetTranslationUnit) {
parent = cTarget.getParent();
}
final ICElement[] containers = new ICElement[cElements.length];
for (int i = 0; i < containers.length; ++i) {
containers[i] = parent;
}
ICElement[] neighbours = null;
if (!isTargetTranslationUnit) {
neighbours = new ICElement[cElements.length];
for (int i = 0; i < neighbours.length; ++i) {
neighbours[i] = cTarget;
}
}
final ICElement[] siblings = neighbours;
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
CoreModel.getDefault().getCModel().copy(cElements, containers, siblings, null, false, monitor);
} catch (CModelException e) {
throw new InvocationTargetException(e);
}
}
};
run(runnable);
return Status.OK_STATUS;
}
return Status.CANCEL_STATUS;
}
private IStatus handleDropMove(final Object target, DropTargetEvent event) throws CModelException, InvocationTargetException, InterruptedException{
ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
final ICElement[] cElements= getCElements(selection);
if (target instanceof ICElement) {
ICElement cTarget = (ICElement)target;
ICElement parent = cTarget;
boolean isTargetTranslationUnit = cTarget instanceof ITranslationUnit;
if (!isTargetTranslationUnit) {
parent = cTarget.getParent();
}
final ICElement[] containers = new ICElement[cElements.length];
for (int i = 0; i < containers.length; ++i) {
containers[i] = parent;
}
ICElement[] neighbours = null;
if (!isTargetTranslationUnit) {
neighbours = new ICElement[cElements.length];
for (int i = 0; i < neighbours.length; ++i) {
neighbours[i] = cTarget;
}
}
final ICElement[] siblings = neighbours;
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
CoreModel.getDefault().getCModel().move(cElements, containers, siblings, null, false, monitor);
} catch (CModelException e) {
throw new InvocationTargetException(e);
}
}
};
run(runnable);
return Status.OK_STATUS;
}
return Status.CANCEL_STATUS;
}
public void run(IRunnableWithProgress runnable) throws InterruptedException, InvocationTargetException {
IRunnableContext context= new ProgressMonitorDialog(getShell());
context.run(true, true, runnable);
}
public static ICElement[] getCElements(ISelection selection) {
if (!(selection instanceof IStructuredSelection)) {
return null;
}
List<?> elements = ((IStructuredSelection)selection).toList();
List<Object> resources= new ArrayList<Object>(elements.size());
for (Object element : elements) {
if (element instanceof ITranslationUnit) {
continue;
}
if (element instanceof ICElement)
resources.add(element);
}
return resources.toArray(new ICElement[resources.size()]);
}
private static boolean canCopyElements(ICElement[] cElements) {
if (cElements != null) {
return hasCommonParent(cElements);
}
return false;
}
private static boolean canMoveElements(ICElement[] cElements) {
if (cElements != null) {
return hasCommonParent(cElements);
}
return false;
}
private static boolean hasCommonParent(ICElement[] elements) {
if (elements.length > 1) {
ICElement parent = elements[0];
for (int i = 0; i < elements.length; ++i) {
ICElement p = elements[i].getParent();
if (parent == null) {
if (p!= null)
return false;
}
else if (!parent.equals(p))
return false;
}
}
return true;
}
private IContainer getDestination(Object dropTarget) {
if (dropTarget instanceof IContainer) {
return (IContainer)dropTarget;
} else if (dropTarget instanceof ICElement) {
return getDestination(((ICElement)dropTarget).getResource());
} else if (dropTarget instanceof IFile) {
return ((IFile)dropTarget).getParent();
}
return null;
}
/**
* Returns the resource selection from the LocalSelectionTransfer.
*
* @return the resource selection from the LocalSelectionTransfer
*/
private IResource[] getSelectedResources() {
ISelection selection = LocalSelectionTransfer.getTransfer()
.getSelection();
if (selection instanceof IStructuredSelection) {
return getSelectedResources((IStructuredSelection)selection);
}
return NO_RESOURCES;
}
/**
* Returns the resource selection from the LocalSelectionTransfer.
*
* @return the resource selection from the LocalSelectionTransfer
*/
private IResource[] getSelectedResources(IStructuredSelection selection) {
ArrayList<Object> selectedResources = new ArrayList<Object>();
for (Iterator<?> i = selection.iterator(); i.hasNext();) {
Object o = i.next();
if (o instanceof IResource) {
selectedResources.add(o);
} else if (o instanceof IAdaptable) {
IAdaptable a = (IAdaptable) o;
IResource r = (IResource) a.getAdapter(IResource.class);
if (r != null) {
selectedResources.add(r);
}
}
}
return selectedResources
.toArray(new IResource[selectedResources.size()]);
}
}