/***************************************************************************** * Copyright (c) 2006, 2007 g-Eclipse Consortium * 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 * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Mathias Stuempert - initial API and implementation *****************************************************************************/ package eu.geclipse.ui.internal.transfer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.util.TransferDropTargetListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.views.navigator.LocalSelectionTransfer; import eu.geclipse.core.model.IGridConnection; import eu.geclipse.core.model.IGridContainer; import eu.geclipse.core.model.IGridElement; import eu.geclipse.core.model.IGridJob; /** * Transfer drop adapter for selections specialised for the transfer of * Grid elements. */ public class SelectionTransferDropAdapter implements TransferDropTargetListener { /** * The current target of the DND-operation. */ private IGridContainer lastTarget; /** * The last detail of the transfer. */ private int lastDetail; /* (non-Javadoc) * @see org.eclipse.jface.util.TransferDropTargetListener#getTransfer() */ public Transfer getTransfer() { return LocalSelectionTransfer.getInstance(); } /* (non-Javadoc) * @see org.eclipse.jface.util.TransferDropTargetListener#isEnabled(org.eclipse.swt.dnd.DropTargetEvent) */ public boolean isEnabled( final DropTargetEvent event ) { IGridContainer target = getTarget( event ); return target != null; } /* (non-Javadoc) * @see org.eclipse.swt.dnd.DropTargetListener#dragEnter(org.eclipse.swt.dnd.DropTargetEvent) */ public void dragEnter( final DropTargetEvent event ) { this.lastTarget = getTarget( event ); this.lastDetail = event.detail; validateDrop( event ); } /* (non-Javadoc) * @see org.eclipse.swt.dnd.DropTargetListener#dragLeave(org.eclipse.swt.dnd.DropTargetEvent) */ public void dragLeave( final DropTargetEvent event ) { this.lastTarget = null; this.lastDetail = DND.DROP_NONE; } /* (non-Javadoc) * @see org.eclipse.swt.dnd.DropTargetListener#dragOperationChanged(org.eclipse.swt.dnd.DropTargetEvent) */ public void dragOperationChanged( final DropTargetEvent event ) { this.lastDetail = event.detail; validateDrop( event ); computeFeedback( event ); } /* (non-Javadoc) * @see org.eclipse.swt.dnd.DropTargetListener#dragOver(org.eclipse.swt.dnd.DropTargetEvent) */ public void dragOver( final DropTargetEvent event ) { IGridContainer newTarget = getTarget( event ); if ( newTarget != this.lastTarget ) { this.lastTarget = newTarget; validateDrop( event ); } computeFeedback( event ); } /* (non-Javadoc) * @see org.eclipse.swt.dnd.DropTargetListener#drop(org.eclipse.swt.dnd.DropTargetEvent) */ public void drop( final DropTargetEvent event ) { IGridContainer target = getTarget( event ); IGridElement[] elements = getElements(); GridElementTransferOperation op = null; switch ( event.detail ) { case DND.DROP_COPY: op = new GridElementTransferOperation( elements, target, false ); break; case DND.DROP_MOVE: op = new GridElementTransferOperation( elements, target, true ); break; } if ( op != null ) { op.setUser( true ); op.setPriority( Job.LONG ); op.schedule(); } else { event.detail = DND.DROP_NONE; } } /* (non-Javadoc) * @see org.eclipse.swt.dnd.DropTargetListener#dropAccept(org.eclipse.swt.dnd.DropTargetEvent) */ public void dropAccept( final DropTargetEvent event ) { this.lastTarget = getTarget( event ); validateDrop( event ); this.lastDetail = event.detail; } /** * Compute the drag over effect feedback for the specified * {@link DropTargetEvent}. * * @param event The event to apply the computed feedback. */ protected void computeFeedback( final DropTargetEvent event ) { event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL | DND.FEEDBACK_SELECT; } /** * Get the Grid elements that are contained in the specified * {@link DropTargetEvent}. This methods returns <code>null</code> * if not all dragged elements are {@link IGridElement}s. * * @param event The event from which to get the elements. * @return All Grid element that are contained in the specified * event. */ protected IGridElement[] getElements() { IGridElement[] result = null; ISelection selection = LocalSelectionTransfer.getInstance().getSelection(); if ( selection instanceof IStructuredSelection ) { IStructuredSelection sSelection = ( IStructuredSelection ) selection; Iterator< ? > iter = sSelection.iterator(); List< IGridElement > elements = new ArrayList< IGridElement >(); while ( iter.hasNext() ) { Object obj = iter.next(); if ( obj instanceof IGridElement ) { elements.add( ( IGridElement ) obj ); } } if ( elements.size() == sSelection.size() ) { result = elements.toArray( new IGridElement[ elements.size() ] ); } } return result; } /** * Get the target that is contained in the specified event. If the * target is not present or the target is not an {@link IGridContainer} * this method returns <code>null</code>. * * @param event The event from which the retrieve the target. * @return The current target of the DND-operation. */ protected IGridContainer getTarget( final DropTargetEvent event ) { IGridContainer result = null; Widget item = event.item; if ( item != null ) { Object data = item.getData(); if ( ( data != null ) && ( data instanceof IGridContainer ) ) { result = ( IGridContainer ) data; } } return result; } /** * Validate the drop, i.e. set the detail field of the specified * {@link DropTargetEvent} according to the policies of the * Grid model. * * @param event The event to be modified. */ protected void validateDrop( final DropTargetEvent event ) { int ops = computeDropOperations( event ); //if ( ( ops & event.detail ) == 0 ) { if ( ( ops & this.lastDetail ) != 0 ) { event.detail = this.lastDetail; } else { if ( ( ops & DND.DROP_DEFAULT ) != 0 ) { event.detail = DND.DROP_DEFAULT; } else if ( ( ops & DND.DROP_MOVE ) != 0 ) { event.detail = DND.DROP_MOVE; } else if ( ( ops & DND.DROP_COPY ) != 0 ) { event.detail = DND.DROP_COPY; } else { event.detail = DND.DROP_NONE; } } } /** * Compute the drop operation for the dragged elements. * * @param event The drop target event. * @return The new drop operations. */ protected int computeDropOperations( final DropTargetEvent event ) { int ops = DND.DROP_NONE; if ( this.lastTarget != null ) { IGridElement[] elements = getElements(); if ( ( elements != null ) && ( elements.length > 0 ) ) { ops = event.operations; for ( IGridElement element : elements ) { ops &= computeDropOperations( this.lastTarget, element ); if ( ops == DND.DROP_NONE ) { break; } } } } return ops; } /** * Compute the drop operations that are allowed for the specified * combination of target and element. * * @param target The target of the drop operation. * @param element The element beeing dragged. * @return A bitwise combination of {@link DND.DROP_COPY} and * {@link DND.DROP_MOVE} or simply {@link DND.DROP_NONE}. */ protected int computeDropOperations( final IGridContainer target, final IGridElement element ) { int result = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT; if ( ( target == element ) || ( target == element.getParent() ) || !target.canContain( element ) || isGridJob( target ) ) { result = DND.DROP_NONE; } else { boolean tLocal = isLocal( target ); boolean eLocal = isLocal( element ); if ( !tLocal || !eLocal ) { // remote -> local || local -> remote || remote -> remote if ( !tLocal && !eLocal ) { // remote -> remote IGridElement tParent = findLastRemoteElement( target ); IGridElement eParent = findLastRemoteElement( element ); if ( tParent != eParent ) { // different connections result = DND.DROP_COPY; } // else result = default } else { // remote -> local || local -> remote if ( !eLocal ) { // remote -> local IGridElement eParent = findLastRemoteElement( element ); if ( eParent != element ) { // not a connection root result = DND.DROP_COPY; } // else result = default } else { // local -> remote result = DND.DROP_COPY; } } } } return result; } /** * Find the last remote parent in the list of parents of the specified * element. This method walks down the list of parents of the element and * returns the first element whose {@link IGridElement#isLocal()} method * returns false. * * @param element The element to be queried. * @return The last parent of the specified element that is not local. */ private IGridElement findLastRemoteElement( final IGridElement element ) { IGridElement result = element; IGridElement parent = element.getParent(); while ( ( parent != null ) && !parent.isLocal() ) { result = parent; parent = parent.getParent(); } return result; } private boolean isLocal( final IGridElement element ) { boolean local = false; if( element.isLocal() ) { local = true; } else { if( element instanceof IGridConnection ) { IGridConnection connection = ( IGridConnection )element; try { IFileStore fileStore = connection.getConnectionFileStore(); if( fileStore != null ) { local = EFS.SCHEME_FILE.equals( fileStore.getFileSystem().getScheme() ); } } catch( CoreException exception ) { // ignore exceptions } } } return local; } private boolean isGridJob( final IGridContainer element ) { boolean isGridJob = false; IGridContainer currentElement = element; while( currentElement != null && !isGridJob ) { isGridJob = ( currentElement instanceof IGridJob ); currentElement = currentElement.getParent(); } return isGridJob; } }