/*=============================================================================#
# Copyright (c) 2000-2016 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 de.walware.ecommons.ltk.ui.util;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.Position;
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.StructuredViewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.participants.CopyProcessor;
import org.eclipse.ltk.core.refactoring.participants.CopyRefactoring;
import org.eclipse.ltk.core.refactoring.participants.MoveProcessor;
import org.eclipse.ltk.core.refactoring.participants.MoveRefactoring;
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.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.statushandlers.StatusManager;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.ecommons.ltk.core.ElementSet;
import de.walware.ecommons.ltk.core.model.ISourceElement;
import de.walware.ecommons.ltk.core.model.ISourceStructElement;
import de.walware.ecommons.ltk.core.model.ISourceUnit;
import de.walware.ecommons.ltk.core.refactoring.CommonRefactoringFactory;
import de.walware.ecommons.ltk.core.refactoring.RefactoringAdapter;
import de.walware.ecommons.ltk.core.refactoring.RefactoringDestination;
import de.walware.ecommons.ltk.internal.ui.refactoring.Messages;
import de.walware.ecommons.ltk.ui.refactoring.RefactoringExecutionHelper;
import de.walware.ecommons.ltk.ui.sourceediting.ISourceEditor;
import de.walware.ecommons.ltk.ui.sourceediting.ISourceEditorAssociated;
public class ViewerSelectionTransferDropAdapter extends ViewerDropAdapter implements TransferDropTargetListener {
private final IAdaptable fPart;
private final CommonRefactoringFactory fRefactoring;
private RefactoringAdapter fAdapter;
private ElementSet fElements;
private MoveProcessor fMoveProcessor;
private int fCanMoveElements;
private CopyProcessor fCopyProcessor;
private int fCanCopyElements;
private ISelection fSelection;
public ViewerSelectionTransferDropAdapter(final StructuredViewer viewer,
final CommonRefactoringFactory refactoring) {
this(viewer, null, refactoring);
}
public ViewerSelectionTransferDropAdapter(final StructuredViewer viewer, final IAdaptable part,
final CommonRefactoringFactory refactoring) {
super(viewer);
fPart = part;
fRefactoring = refactoring;
setScrollEnabled(true);
setExpandEnabled(true);
setSelectionFeedbackEnabled(false);
setFeedbackEnabled(false);
}
//---- TransferDropTargetListener interface ---------------------------------------
@Override
public Transfer getTransfer() {
return LocalSelectionTransfer.getTransfer();
}
@Override
public boolean isEnabled(final DropTargetEvent event) {
final Object target = event.item != null ? event.item.getData() : null;
if (target == null) {
return false;
}
return (target instanceof ISourceStructElement);
}
//---- Actual DND -----------------------------------------------------------------
@Override
public void dragEnter(final DropTargetEvent event) {
clear();
super.dragEnter(event);
}
@Override
public void dragLeave(final DropTargetEvent event) {
clear();
super.dragLeave(event);
}
private void clear() {
setSelectionFeedbackEnabled(false);
fElements = null;
fSelection = null;
fMoveProcessor = null;
fCanMoveElements = 0;
fCopyProcessor = null;
fCanCopyElements = 0;
fAdapter = null;
}
@Override
public boolean validateDrop(final Object target, final int operation, final TransferData transferType) {
final int result = internalDetermineOperation(target, operation,
DND.DROP_MOVE | DND.DROP_COPY);
if (result == DND.DROP_NONE) {
setSelectionFeedbackEnabled(false);
return false;
}
else {
setSelectionFeedbackEnabled(true);
overrideOperation(result);
return true;
}
}
private int internalDetermineOperation(final Object target, final int operation, final int operations) {
if (!(target instanceof ISourceElement)) {
return DND.DROP_NONE;
}
if (!initializeSelection()) {
return DND.DROP_NONE;
}
if (fElements.getResources().size() > 0) { // resources not yet supported
return DND.DROP_NONE;
}
fElements.removeElementsWithAncestorsOnList();
RefactoringDestination.Position pos;
switch (getCurrentLocation()) {
case LOCATION_BEFORE:
pos = RefactoringDestination.Position.ABOVE;
break;
case LOCATION_AFTER:
pos = RefactoringDestination.Position.BELOW;
break;
default:
pos = RefactoringDestination.Position.INTO;
}
final RefactoringDestination destination = new RefactoringDestination(target, pos);
fAdapter = fRefactoring.createAdapter(destination);
if (fAdapter == null || !fAdapter.canInsert(fElements, destination)) {
return DND.DROP_NONE;
}
try {
switch (operation) {
case DND.DROP_DEFAULT:
return handleValidateDefault(destination, operations);
case DND.DROP_COPY:
return handleValidateCopy(destination);
case DND.DROP_MOVE:
return handleValidateMove(destination);
}
}
catch (final CoreException e) {
if (e.getStatus().getSeverity() == IStatus.ERROR) {
StatusManager.getManager().handle(new Status(IStatus.ERROR,
fAdapter.getPluginIdentifier(), -1,
"An error occurred when validation the drop location.", e ));
}
}
return DND.DROP_NONE;
}
protected boolean initializeSelection(){
if (fElements != null) {
return fElements.isOK();
}
final ISelection s = LocalSelectionTransfer.getTransfer().getSelection();
if (!(s instanceof IStructuredSelection)) {
return false;
}
fSelection = s;
fElements = new ElementSet(((IStructuredSelection) s).toArray());
if (!fElements.isOK()) {
return false;
}
return true;
}
protected ISelection getSelection(){
return fSelection;
}
@Override
public boolean performDrop(final Object data) {
switch(getCurrentOperation()) {
case DND.DROP_MOVE:
return handleDropMove();
case DND.DROP_COPY:
return handleDropCopy();
}
return false;
}
private int handleValidateDefault(final RefactoringDestination destination,
final int operations) throws CoreException {
if ((operations & DND.DROP_MOVE) != 0) {
final int result = handleValidateMove(destination);
if (result != DND.DROP_NONE) {
return result;
}
}
return handleValidateCopy(destination);
}
private int handleValidateMove(final RefactoringDestination destination) throws CoreException {
if (fMoveProcessor == null) {
final MoveProcessor processor = fRefactoring.createMoveProcessor(fElements, destination, fAdapter);
if (processor != null && processor.isApplicable()) {
fMoveProcessor = processor;
}
}
return (canMoveElements()) ? DND.DROP_MOVE : DND.DROP_NONE;
}
private boolean canMoveElements() {
if (fCanMoveElements == 0) {
fCanMoveElements = (fMoveProcessor != null) ? 2 : 1;
}
return fCanMoveElements == 2;
}
protected boolean handleDropMove() {
try {
execute(new MoveRefactoring(fMoveProcessor));
return true;
}
catch (final InvocationTargetException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR,
fAdapter.getPluginIdentifier(), -1,
Messages.MoveElements_error_message, e.getCause() ),
StatusManager.LOG | StatusManager.SHOW );
return false;
}
catch (final InterruptedException e) {
return false;
}
}
private int handleValidateCopy(final RefactoringDestination destination) throws CoreException {
if (fCopyProcessor == null) {
final CopyProcessor processor = fRefactoring.createCopyProcessor(fElements, destination, fAdapter);
if (processor != null && processor.isApplicable()) {
fCopyProcessor = processor;
}
}
return (canCopyElements()) ? DND.DROP_COPY : DND.DROP_NONE;
}
private boolean canCopyElements() {
if (fCanCopyElements == 0) {
fCanCopyElements = (fCopyProcessor != null) ? 2 : 1;
}
return fCanCopyElements == 2;
}
protected boolean handleDropCopy() {
try {
execute(new CopyRefactoring(fCopyProcessor));
return true;
}
catch (final InvocationTargetException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR,
fAdapter.getPluginIdentifier(), -1,
Messages.CopyElements_error_message, e.getCause() ),
StatusManager.LOG | StatusManager.SHOW );
return false;
}
catch (final InterruptedException e) {
return false;
}
}
protected void execute(final Refactoring refactoring) throws InterruptedException, InvocationTargetException {
final IWorkbenchWindow window = UIAccess.getActiveWorkbenchWindow(true);
final IProgressService context = (IProgressService) window.getService(IProgressService.class);
final RefactoringExecutionHelper helper = new RefactoringExecutionHelper(refactoring,
RefactoringCore.getConditionCheckingFailedSeverity(),
getShell(), context );
ISourceEditor editor = null;
if (fPart != null) {
editor = (ISourceEditor) fPart.getAdapter(ISourceEditor.class);
if (editor == null) {
final ISourceEditorAssociated associated = (ISourceEditorAssociated) fPart
.getAdapter(ISourceEditorAssociated.class);
if (associated != null) {
editor = associated.getSourceEditor();
}
}
}
if (editor != null) {
final ISourceUnit su = editor.getSourceUnit();
if (su != null) {
helper.enableInsertPosition(su);
}
}
helper.perform(false, false);
if (editor != null) {
final Position position = helper.getInsertPosition();
if (position != null) {
editor.selectAndReveal(position.getOffset(), 0);
}
}
}
private Shell getShell() {
return getViewer().getControl().getShell();
}
@Override
protected int getCurrentLocation() {
if (getFeedbackEnabled()) {
return super.getCurrentLocation();
}
else {
return LOCATION_ON;
}
}
}