blob: e8490a2fda1ec1f712f174d94489ae515de6619e [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2000, 2020 IBM Corporation and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# IBM Corporation - org.eclipse.jdt: initial API and implementation
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.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 org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.internal.ltk.ui.refactoring.Messages;
import org.eclipse.statet.ltk.model.core.ElementSet;
import org.eclipse.statet.ltk.model.core.element.SourceElement;
import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
import org.eclipse.statet.ltk.refactoring.core.CommonRefactoringFactory;
import org.eclipse.statet.ltk.refactoring.core.RefactoringAdapter;
import org.eclipse.statet.ltk.refactoring.core.RefactoringDestination;
import org.eclipse.statet.ltk.ui.refactoring.RefactoringExecutionHelper;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorAssociated;
public class ViewerSelectionTransferDropAdapter extends ViewerDropAdapter implements TransferDropTargetListener {
private final IAdaptable part;
private final CommonRefactoringFactory refactoring;
private RefactoringAdapter adapter;
private ElementSet elements;
private MoveProcessor moveProcessor;
private int canMoveElements;
private CopyProcessor copyProcessor;
private int canCopyElements;
private ISelection selection;
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);
this.part= part;
this.refactoring= 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 SourceStructElement);
}
//---- 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);
this.elements= null;
this.selection= null;
this.moveProcessor= null;
this.canMoveElements= 0;
this.copyProcessor= null;
this.canCopyElements= 0;
this.adapter= 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 SourceElement)) {
return DND.DROP_NONE;
}
if (!initializeSelection()) {
return DND.DROP_NONE;
}
if (this.elements.getResources().size() > 0) { // resources not yet supported
return DND.DROP_NONE;
}
this.elements.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);
this.adapter= this.refactoring.createAdapter(destination);
if (this.adapter == null || !this.adapter.canInsert(this.elements, 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,
this.adapter.getPluginIdentifier(), -1,
"An error occurred when validation the drop location.", e ));
}
}
return DND.DROP_NONE;
}
protected boolean initializeSelection(){
if (this.elements != null) {
return this.elements.isOK();
}
final ISelection s= LocalSelectionTransfer.getTransfer().getSelection();
if (!(s instanceof IStructuredSelection)) {
return false;
}
this.selection= s;
this.elements= new ElementSet(((IStructuredSelection) s).toArray());
if (!this.elements.isOK()) {
return false;
}
return true;
}
protected ISelection getSelection(){
return this.selection;
}
@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 (this.moveProcessor == null) {
final MoveProcessor processor= this.refactoring.createMoveProcessor(this.elements, destination, this.adapter);
if (processor != null && processor.isApplicable()) {
this.moveProcessor= processor;
}
}
return (canMoveElements()) ? DND.DROP_MOVE : DND.DROP_NONE;
}
private boolean canMoveElements() {
if (this.canMoveElements == 0) {
this.canMoveElements= (this.moveProcessor != null) ? 2 : 1;
}
return this.canMoveElements == 2;
}
protected boolean handleDropMove() {
try {
execute(new MoveRefactoring(this.moveProcessor));
return true;
}
catch (final InvocationTargetException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR,
this.adapter.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 (this.copyProcessor == null) {
final CopyProcessor processor= this.refactoring.createCopyProcessor(this.elements, destination, this.adapter);
if (processor != null && processor.isApplicable()) {
this.copyProcessor= processor;
}
}
return (canCopyElements()) ? DND.DROP_COPY : DND.DROP_NONE;
}
private boolean canCopyElements() {
if (this.canCopyElements == 0) {
this.canCopyElements= (this.copyProcessor != null) ? 2 : 1;
}
return this.canCopyElements == 2;
}
protected boolean handleDropCopy() {
try {
execute(new CopyRefactoring(this.copyProcessor));
return true;
}
catch (final InvocationTargetException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR,
this.adapter.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= window.getService(IProgressService.class);
final RefactoringExecutionHelper helper= new RefactoringExecutionHelper(refactoring,
RefactoringCore.getConditionCheckingFailedSeverity(),
getShell(), context );
ISourceEditor editor= null;
if (this.part != null) {
editor= this.part.getAdapter(ISourceEditor.class);
if (editor == null) {
final ISourceEditorAssociated associated= this.part
.getAdapter(ISourceEditorAssociated.class);
if (associated != null) {
editor= associated.getSourceEditor();
}
}
}
if (editor != null) {
final SourceUnit 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;
}
}
}