/******************************************************************************* * Copyright (c) 2008, 2010 Andrew Gvozdev. * 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: * Andrew Gvozdev (Quoin Inc.) - Initial implementation *******************************************************************************/ package org.eclipse.cdt.make.internal.ui.dnd; import org.eclipse.cdt.make.core.IMakeTarget; import org.eclipse.cdt.make.ui.TargetSourceContainer; import org.eclipse.core.resources.IContainer; import org.eclipse.jface.util.TransferDropTargetListener; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.DropTargetListener; import org.eclipse.swt.dnd.TransferData; /** * This abstract class provides a frame for {@link DropTargetListener} able to accept a * drop into a container area. A container area includes the container itself and * the items directly belonging to the container. A drop into the container area is * treated as a drop on the container itself. Also, some flexibility for adjusting * drop operation is provided. * * @see org.eclipse.swt.dnd.DropTargetListener * */ public abstract class AbstractContainerAreaDropAdapter implements TransferDropTargetListener { private int originallyRequestedOperation = DND.DROP_NONE; private Object lastDragOverTarget = null; private int lastDragOverOperation = DND.DROP_NONE; /** * This method lets changing initial drag operation. The operation will be * indicated by mouse cursor. This operation is passed in {@code event.detail} * to {@code dragEnter} method. * * @param operation - incoming operation. * @return changed operation. The return must be one of * {@link org.eclipse.swt.dnd.DND} operations. * * @see DropTargetListener#dragEnter(DropTargetEvent) * @see DND#DROP_NONE * @see DND#DROP_COPY * @see DND#DROP_MOVE * @see DND#DROP_LINK * @see DND#DROP_DEFAULT */ protected abstract int dragEnterOperation(int operation); /** * This method lets adjust drag operation over container area. The operation * will be indicated by mouse cursor. This operation is passed in {@code * event.detail} to {@code dragOver} method. * * @param operation - incoming operation. * @param dropContainer - container where drop is going to be. * @param dropTarget - drop target. * @return changed operation. The return must be one of * {@link org.eclipse.swt.dnd.DND} operations. * * * @see DropTargetListener#dragOver(DropTargetEvent) * @see DND#DROP_NONE * @see DND#DROP_COPY * @see DND#DROP_MOVE * @see DND#DROP_LINK * @see DND#DROP_DEFAULT */ protected abstract int dragOverOperation(int operation, IContainer dropContainer, Object dropTarget); /** * Implementation of the actual drop of {@code dropObject} to {@code dropContainer}. * * @param dropObject - object to drop. * @param dropContainer - container where to drop the object. * @param operation - drop operation. * * @see DropTargetListener#drop(DropTargetEvent) */ protected abstract void dropToContainer(Object dropObject, IContainer dropContainer, int operation); /** * Defines if a transfer data is supported by the implementer. * * @see org.eclipse.swt.dnd.Transfer#isSupportedType * * @param transferData - data type to examine * @return whether the given data type is supported by this listener */ public boolean isSupportedType(TransferData transferData) { return getTransfer().isSupportedType(transferData); } /** * This function is called only the listener is able to handle provided data type. * By default {@code isEnabled} returns {@code true} but subclasses are free to * override. * * @param event - the information associated with the drag event. * @return {@code true} * * @see DropTargetEvent */ public boolean isEnabled(DropTargetEvent event) { return true; } /** * Implementation of {@code DropTargetListener#dragEnter} to support * dropping into container area. * * @param event - the information associated with the drag event * * @see DropTargetEvent */ public void dragEnter(DropTargetEvent event) { lastDragOverTarget = null; lastDragOverOperation = DND.DROP_NONE; if (isSupportedType(event.currentDataType)) { originallyRequestedOperation = event.detail; event.detail = dragEnterOperation(originallyRequestedOperation); } else { event.detail = DND.DROP_NONE; originallyRequestedOperation = DND.DROP_NONE; } } /** * Implementation of {@code DropTargetListener#dragOperationChanged} to support * dropping into container area. * * @param event - the information associated with the drag event * * @see DropTargetEvent */ public void dragOperationChanged(DropTargetEvent event) { originallyRequestedOperation = event.detail; event.detail = dragOverOperationCached(originallyRequestedOperation, determineDropContainer(event), determineDropTarget(event)); } /** * Implementation of {@code DropTargetListener#dragOver} to support * dropping into container area. * * @param event - the information associated with the drag event * * @see DropTargetEvent */ public void dragOver(DropTargetEvent event) { event.detail = dragOverOperationCached(originallyRequestedOperation, determineDropContainer(event), determineDropTarget(event)); if (originallyRequestedOperation != DND.DROP_NONE) { // let user discover items even if event.detail is DND.DROP_NONE // since the original operation could be applicable to other items event.feedback = DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; if (event.detail != DND.DROP_NONE) { event.feedback = event.feedback | DND.FEEDBACK_SELECT; } } else { event.feedback = DND.FEEDBACK_NONE; } } /** * Implementation of {@code DropTargetListener#dragLeave} to support * dropping into container area. * * @param event - the information associated with the drag event * * @see DropTargetEvent */ public void dragLeave(DropTargetEvent event) { // no action } /** * Implementation of {@code DropTargetListener#dropAccept} to support * dropping into container area. * * @param event - the information associated with the drag event * * @see DropTargetEvent */ public void dropAccept(DropTargetEvent event) { // no action } /** * Implementation of {@code DropTargetListener#drop} to support * dropping into container area. * * @param event - the information associated with the drag event * * @see DropTargetEvent */ public void drop(DropTargetEvent event) { IContainer dropContainer = determineDropContainer(event); if (dropContainer != null) { event.detail = dragOverOperationCached(event.detail, dropContainer, determineDropTarget(event)); dropToContainer(event.data, dropContainer, event.detail); } else { event.detail = DND.DROP_NONE; } } /** * This method provides caching of potentially long running and called on each * mouse move {@link #dragOverOperation}. * * @param operation - incoming operation. * @param dropContainer - container where drop is going to be. * @param dropTarget - drop target. * @return changed operation. The return must be one of * org.eclipse.swt.dnd.DND operations such as {@link DND#DROP_NONE}, * {@link DND#DROP_COPY}, {@link DND#DROP_MOVE}, * {@link DND#DROP_LINK} * * * @see DropTargetListener#dragOver(DropTargetEvent) */ private int dragOverOperationCached(int operation, IContainer dropContainer, Object dropTarget) { if (dropTarget != lastDragOverTarget || operation != lastDragOverOperation) { lastDragOverOperation = dragOverOperation(operation, dropContainer, dropTarget); lastDragOverTarget = dropTarget; } return lastDragOverOperation; } /** * Returns the drop target passed by {@code event.item}. * * @param event - the information associated with the drop event */ private static Object determineDropTarget(DropTargetEvent event) { return event.item == null ? null : event.item.getData(); } /** * Find which container area the mouse cursor is currently in. * * @param event - the information associated with the drop event * @return the container of the current mouse location. */ public static IContainer determineDropContainer(DropTargetEvent event) { Object dropTarget = determineDropTarget(event); if (dropTarget instanceof IMakeTarget) { return ((IMakeTarget) dropTarget).getContainer(); } else if (dropTarget instanceof IContainer) { return (IContainer) dropTarget; } else if (dropTarget instanceof TargetSourceContainer) { IContainer dropContainer = ((TargetSourceContainer) dropTarget).getContainer(); return dropContainer; } return null; } }