blob: 5edacde426d4718edee82a3533839923d4e23f19 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.ui.packageview;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.internal.corext.refactoring.reorg.ReorgUtils;
import org.eclipse.wst.jsdt.internal.ui.dnd.JdtViewerDropAdapter;
import org.eclipse.wst.jsdt.internal.ui.workingsets.OthersWorkingSetUpdater;
import org.eclipse.wst.jsdt.internal.ui.workingsets.WorkingSetModel;
public class WorkingSetDropAdapter extends JdtViewerDropAdapter implements TransferDropTargetListener {
private PackageExplorerPart fPackageExplorer;
private IStructuredSelection fSelection;
private Object[] fElementsToAdds;
private Set fCurrentElements;
private IWorkingSet fWorkingSet;
public WorkingSetDropAdapter(PackageExplorerPart part) {
super(part.getTreeViewer(), DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND);
fPackageExplorer= part;
}
//---- TransferDropTargetListener interface ---------------------------------------
public Transfer getTransfer() {
return LocalSelectionTransfer.getTransfer();
}
public boolean isEnabled(DropTargetEvent event) {
Object target= event.item != null ? event.item.getData() : null;
if (target == null)
return false;
ISelection selection= LocalSelectionTransfer.getTransfer().getSelection();
if (!isValidSelection(selection)) {
return false;
}
if (!isValidTarget(target))
return false;
initializeState(target, selection);
return true;
}
//---- Actual DND -----------------------------------------------------------------
public void validateDrop(Object target, DropTargetEvent event, int operation) {
event.detail= DND.DROP_NONE;
switch(operation) {
case DND.DROP_DEFAULT:
case DND.DROP_COPY:
case DND.DROP_MOVE:
event.detail= validateTarget(target, operation);
break;
case DND.DROP_LINK:
event.detail= DND.DROP_NONE;
break;
}
}
private int validateTarget(Object target, int operation) {
showInsertionFeedback(false);
setDefaultFeedback(DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND);
if (!isValidTarget(target))
return DND.DROP_NONE;
ISelection s= LocalSelectionTransfer.getTransfer().getSelection();
if (!isValidSelection(s)) {
return DND.DROP_NONE;
}
initializeState(target, s);
if (isWorkingSetSelection()) {
setDefaultFeedback(DND.FEEDBACK_SCROLL);
if (fLocation == LOCATION_BEFORE || fLocation == LOCATION_AFTER) {
showInsertionFeedback(true);
return DND.DROP_MOVE;
}
return DND.DROP_NONE;
} else {
if (isOthersWorkingSet(fWorkingSet) && operation == DND.DROP_COPY)
return DND.DROP_NONE;
List realJavaElements= new ArrayList();
List realResource= new ArrayList();
ReorgUtils.splitIntoJavaElementsAndResources(fElementsToAdds, realJavaElements, realResource);
if (fElementsToAdds.length != realJavaElements.size() + realResource.size())
return DND.DROP_NONE;
for (Iterator iter= realJavaElements.iterator(); iter.hasNext();) {
IJavaScriptElement element= (IJavaScriptElement)iter.next();
if (ReorgUtils.containsElementOrParent(fCurrentElements, element))
return DND.DROP_NONE;
}
for (Iterator iter= realResource.iterator(); iter.hasNext();) {
IResource element= (IResource)iter.next();
if (ReorgUtils.containsElementOrParent(fCurrentElements, element))
return DND.DROP_NONE;
}
if (!(fSelection instanceof ITreeSelection)) {
return DND.DROP_COPY;
}
ITreeSelection treeSelection= (ITreeSelection)fSelection;
TreePath[] paths= treeSelection.getPaths();
for (int i= 0; i < paths.length; i++) {
TreePath path= paths[i];
if (path.getSegmentCount() != 2)
return DND.DROP_COPY;
if (!(path.getSegment(0) instanceof IWorkingSet))
return DND.DROP_COPY;
if (paths.length == 1) {
IWorkingSet ws= (IWorkingSet)path.getSegment(0);
if (OthersWorkingSetUpdater.ID.equals(ws.getId()))
return DND.DROP_MOVE;
}
}
}
if (operation == DND.DROP_DEFAULT)
return DND.DROP_MOVE;
return operation;
}
private boolean isValidTarget(Object target) {
return target instanceof IWorkingSet;
}
private boolean isValidSelection(ISelection selection) {
return selection instanceof IStructuredSelection;
}
private boolean isOthersWorkingSet(IWorkingSet ws) {
return OthersWorkingSetUpdater.ID.equals(ws.getId());
}
private void initializeState(Object target, ISelection s) {
fWorkingSet= (IWorkingSet)target;
fSelection= (IStructuredSelection)s;
fElementsToAdds= fSelection.toArray();
fCurrentElements= new HashSet(Arrays.asList(fWorkingSet.getElements()));
}
private boolean isWorkingSetSelection() {
for (int i= 0; i < fElementsToAdds.length; i++) {
if (!(fElementsToAdds[i] instanceof IWorkingSet))
return false;
}
return true;
}
public void drop(Object target, final DropTargetEvent event) {
if (isWorkingSetSelection()) {
performWorkingSetReordering();
} else {
performElementRearrange(event.detail);
}
// drag adapter has nothing to do, even on move.
event.detail= DND.DROP_NONE;
}
private void performWorkingSetReordering() {
WorkingSetModel model= fPackageExplorer.getWorkingSetModel();
List activeWorkingSets= new ArrayList(Arrays.asList(model.getActiveWorkingSets()));
int index= activeWorkingSets.indexOf(fWorkingSet);
if (index != -1) {
if (fLocation == LOCATION_AFTER)
index++;
List result= new ArrayList(activeWorkingSets.size());
List selected= new ArrayList(Arrays.asList(fElementsToAdds));
for (int i= 0; i < activeWorkingSets.size(); i++) {
if (i == index) {
result.addAll(selected);
}
Object element= activeWorkingSets.get(i);
if (!selected.contains(element)) {
result.add(element);
}
}
if (index == activeWorkingSets.size())
result.addAll(selected);
model.setActiveWorkingSets((IWorkingSet[])result.toArray(new IWorkingSet[result.size()]));
}
}
private void performElementRearrange(int eventDetail) {
// only move if target isn't the other working set. If this is the case
// the move will happenn automatically by refreshing the other working set
if (!isOthersWorkingSet(fWorkingSet)) {
List elements= new ArrayList(Arrays.asList(fWorkingSet.getElements()));
elements.addAll(Arrays.asList(fElementsToAdds));
fWorkingSet.setElements((IAdaptable[])elements.toArray(new IAdaptable[elements.size()]));
}
if (eventDetail == DND.DROP_MOVE) {
ITreeSelection treeSelection= (ITreeSelection)fSelection;
Map workingSets= groupByWorkingSets(treeSelection.getPaths());
for (Iterator iter= workingSets.keySet().iterator(); iter.hasNext();) {
IWorkingSet ws= (IWorkingSet)iter.next();
List toRemove= (List)workingSets.get(ws);
List currentElements= new ArrayList(Arrays.asList(ws.getElements()));
currentElements.removeAll(toRemove);
ws.setElements((IAdaptable[])currentElements.toArray(new IAdaptable[currentElements.size()]));
}
}
}
private Map/*<List<IWorkingSet>>*/ groupByWorkingSets(TreePath[] paths) {
Map result= new HashMap();
for (int i= 0; i < paths.length; i++) {
TreePath path= paths[i];
IWorkingSet ws= (IWorkingSet)path.getSegment(0);
List l= (List)result.get(ws);
if (l == null) {
l= new ArrayList();
result.put(ws, l);
}
l.add(path.getSegment(1));
}
return result;
}
//---- test methods for JUnit test since DnD is hard to simulate
public int internalTestValidateTarget(Object target, int operation) {
return validateTarget(target, operation);
}
public void internalTestDrop(Object target, int eventDetail) {
if (isWorkingSetSelection()) {
performWorkingSetReordering();
} else {
performElementRearrange(eventDetail);
}
}
}