/******************************************************************************* * 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.scriptview; 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.dltk.core.IModelElement; import org.eclipse.dltk.internal.corext.refactoring.reorg.ReorgUtils; import org.eclipse.dltk.internal.ui.dnd.DLTKViewerDropAdapter; import org.eclipse.dltk.internal.ui.workingsets.WorkingSetIDs; import org.eclipse.dltk.internal.ui.workingsets.WorkingSetModel; 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.swt.dnd.TransferData; import org.eclipse.ui.IWorkingSet; public class WorkingSetDropAdapter extends DLTKViewerDropAdapter implements TransferDropTargetListener { private ScriptExplorerPart fPackageExplorer; private IStructuredSelection fSelection; private Object[] fElementsToAdds; private Set fCurrentElements; private IWorkingSet fWorkingSet; private int fLocation; public WorkingSetDropAdapter(ScriptExplorerPart part) { super(part.getTreeViewer()); fPackageExplorer = part; fLocation = -1; setScrollEnabled(true); setExpandEnabled(true); setFeedbackEnabled(false); } // ---- TransferDropTargetListener interface // --------------------------------------- @Override public Transfer getTransfer() { return LocalSelectionTransfer.getTransfer(); } @Override 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 // ----------------------------------------------------------------- @Override public boolean validateDrop(Object target, int operation, TransferData transferType) { return determineOperation(target, operation, transferType, DND.DROP_MOVE | DND.DROP_LINK | DND.DROP_COPY) != DND.DROP_NONE; } @Override protected int determineOperation(Object target, int operation, TransferData transferType, int operations) { switch (operation) { case DND.DROP_DEFAULT: case DND.DROP_COPY: case DND.DROP_MOVE: return validateTarget(target, operation); default: return DND.DROP_NONE; } } private int validateTarget(Object target, int operation) { setFeedbackEnabled(false); setScrollEnabled(true); setExpandEnabled(true); if (!isValidTarget(target)) return DND.DROP_NONE; ISelection s = LocalSelectionTransfer.getTransfer().getSelection(); if (!isValidSelection(s)) { return DND.DROP_NONE; } initializeState(target, s); if (isWorkingSetSelection()) { setExpandEnabled(false); if (getCurrentLocation() == LOCATION_BEFORE || getCurrentLocation() == LOCATION_AFTER) { setFeedbackEnabled(true); return DND.DROP_MOVE; } return DND.DROP_NONE; } else { if (isOthersWorkingSet(fWorkingSet) && operation == DND.DROP_COPY) return DND.DROP_NONE; List realScriptElements = new ArrayList(); List realResource = new ArrayList(); ReorgUtils.splitIntoModelElementsAndResources(fElementsToAdds, realScriptElements, realResource); if (fElementsToAdds.length != realScriptElements.size() + realResource.size()) return DND.DROP_NONE; for (Iterator iter = realScriptElements.iterator(); iter .hasNext();) { IModelElement element = (IModelElement) 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 (WorkingSetIDs.OTHERS.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 WorkingSetIDs.OTHERS.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; } /** * {@inheritDoc} */ @Override public boolean performDrop(Object data) { if (isWorkingSetSelection()) { performWorkingSetReordering(); } else { performElementRearrange(getCurrentOperation()); } // drag adapter has nothing to do, even on move. return false; } private void performWorkingSetReordering() { WorkingSetModel model = fPackageExplorer.getWorkingSetModel(); List activeWorkingSets = new ArrayList( Arrays.asList(model.getActiveWorkingSets())); int index = activeWorkingSets.indexOf(fWorkingSet); if (index != -1) { if (getCurrentLocation() == 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<IAdaptable> currentElements = new ArrayList<>( Arrays.asList(ws.getElements())); currentElements.removeAll(toRemove); ws.setElements(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); } } public void internalTestSetLocation(int location) { fLocation = location; } /** * {@inheritDoc} */ @Override protected int getCurrentLocation() { if (fLocation == -1) return super.getCurrentLocation(); return fLocation; } }