| /*=============================================================================# |
| # 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; |
| } |
| } |
| |
| } |