/****************************************************************************** * Copyright (c) 2002, 2009 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 org.eclipse.gmf.runtime.diagram.ui.parts; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.Cursors; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.dnd.AbstractTransferDropTargetListener; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.Transfer; import org.eclipse.gmf.runtime.diagram.ui.requests.DropObjectsRequest; import org.eclipse.gmf.runtime.notation.View; /** * Performs a drop of one or more semantic elements using a given transfer. The Drop is performed by * using a {@link DropObjectsRequest} to obtain a <code>Command</code> from the targeted * <code>EditPart</code>. The target edit part might re-interpret the <code>DropElementsRequest</code> * to mean another request. Note that some transfers on different OS's occur at the drop time, hence * live feedback cannot be provided for these transfers (i.e. command created). If this is the case then to enable * drag and drop on different platforms {@link #isDataTransfered()} needs to be implemented. * <P> * This class is <code>abstract</code>. Subclasses are responsible for providing the * appropriate <code>Transfer</code> object based on the type of elements that are being dragged. * * @author melaasar, aboyko */ public abstract class DiagramDropTargetListener extends AbstractTransferDropTargetListener { /** * Constructor for DiagramDropTargetListener. * @param viewer */ public DiagramDropTargetListener(EditPartViewer viewer) { super(viewer); setEnablementDeterminedByCommand(true); } /** * Constructor for DiagramDropTargetListener. * @param viewer * @param xfer */ public DiagramDropTargetListener(EditPartViewer viewer, Transfer xfer) { super(viewer, xfer); setEnablementDeterminedByCommand(true); } /** * * @see org.eclipse.gef.dnd.AbstractTransferDropTargetListener#createTargetRequest() */ protected Request createTargetRequest() { return new DropObjectsRequest(); } /** * A helper method that casts the target Request to a DropElementsRequest. * @return DropElementsRequest */ protected final DropObjectsRequest getDropObjectsRequest() { return ((DropObjectsRequest) getTargetRequest()); } /** * gets a list of objects being dropped on the diagram * @return <code>List</code> */ protected abstract List getObjectsBeingDropped(); /** * @see org.eclipse.swt.dnd.DropTargetListener#dragEnter(org.eclipse.swt.dnd.DropTargetEvent) */ public void dragEnter(DropTargetEvent event) { super.dragEnter(event); handleDragEnter(); // called to properly initialize the effect } /** * Called whenever the User enters the target. By default, the target Request and * target EditPart are updated, and feedback is */ protected void handleDragEnter() { handleDragOver(); } /** * The purpose of a template is to be copied. Therefore, the drop operation can't be * anything but <code>DND.DROP_COPY</code>. * @see AbstractTransferDropTargetListener#handleDragOperationChanged() */ protected void handleDragOperationChanged() { super.handleDragOperationChanged(); /* * The edit policies creating the command for the drop request may modify the * required detail field, so it's being set for the event here. However, if the command * can't be created due to the fact that data hasn't been transfered yet then the request will * contain DND.DROP_NONE required detail that we don't want to set for the event. */ if (getDropObjectsRequest().getRequiredDetail() != DND.DROP_NONE) { getCurrentEvent().detail = getDropObjectsRequest().getRequiredDetail(); } } /** * The purpose of a template is to be copied. Therefore, the Drop operation is set to * <code>DND.DROP_COPY</code> by default. * @see org.eclipse.gef.dnd.AbstractTransferDropTargetListener#handleDragOver() */ protected void handleDragOver() { super.handleDragOver(); /* * The edit policies creating the command for the drop request may modify the * required detail field, so it's being set for the event here. However, if the command * can't be created due to the fact that data hasn't been transfered yet then the request will * contain DND.DROP_NONE required detail that we don't want to set for the event. */ if (getDropObjectsRequest().getRequiredDetail() != DND.DROP_NONE) { getCurrentEvent().detail = getDropObjectsRequest().getRequiredDetail(); } getCurrentEvent().feedback = DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND; } /** * Overridden to select the request result if any * @see org.eclipse.gef.dnd.AbstractTransferDropTargetListener#handleDrop() */ protected void handleDrop() { getViewer().setCursor(Cursors.WAIT); super.handleDrop(); getViewer().setCursor(null); selectAddedViews(); } /** * Selects the created views that could result from the drop request if any */ private void selectAddedViews() { Object result = getDropObjectsRequest().getResult(); if (result == null || !(result instanceof Collection)) return; EditPartViewer viewer = getViewer(); List editParts = new ArrayList(); Iterator views = ((Collection)result).iterator(); while (views.hasNext()) { Object view = views.next(); if (view instanceof IAdaptable) { EditPart editPart = (EditPart) viewer.getEditPartRegistry().get(((IAdaptable)view).getAdapter(View.class)); if (editPart != null) editParts.add(editPart); } } if (!editParts.isEmpty()) { //Force a layout first. viewer.getControl().forceFocus(); getViewer().flush(); viewer.setSelection(new StructuredSelection(editParts)); } } /** * Assumes that the target request is a {@link DropObjectsRequest}. GEF * wipes out the request in {@link #isEnabled(DropTargetEvent)} method, we * don't. Hence we just update the necessary fields: <li>the mouse location * <li>the objects being dropped <li>the allowed detail that comes from the * DND event * * @see org.eclipse.gef.dnd.AbstractTransferDropTargetListener# * updateTargetRequest() */ protected void updateTargetRequest() { DropObjectsRequest request = getDropObjectsRequest(); request.setLocation(getDropLocation()); request.setObjects(getObjectsBeingDropped()); request.setAllowedDetail(getCurrentEvent().operations); } /* (non-Javadoc) * @see org.eclipse.gef.dnd.AbstractTransferDropTargetListener#isEnabled(org.eclipse.swt.dnd.DropTargetEvent) */ public boolean isEnabled(DropTargetEvent event) { /* * Cache the current event */ setCurrentEvent(event); /* * Update the target request and target editpart */ updateTargetRequest(); updateTargetEditPart(); if (getTargetEditPart() == null) { return false; } else if (isEnablementDeterminedByCommand() && isDataTransfered()) { /* * Check the command only if: * 1) The data has been transfered from the drag source to the event * 2) The "ask for command" option is on. (It's "on" by default) */ Command command = getCommand(); return command != null && command.canExecute(); } else { /* * Otherwise we should enable the drop. Executable command needs to be created at the drop time anyway. * Hence, we'll fail the drop there if there is no target editpart or no data transfered. */ return true; } } /** * It is not a common use case to have the transfered data at the drag time, * hence live feedback cannot be provided for all types of DnD. Since the * occurrence of the data transfer at the drag time depends mostly on the * used OS and the the type of data transfer, clients are responsible to * experiment with their DnD and come up with the appropriate implementation * of this method for their specific DnD support. By default the method * checks if there is a local selection transfer or data field in the event * is not null. * * @return <code>true</code> if data has been transfered. */ protected boolean isDataTransfered() { return LocalSelectionTransfer.getTransfer().getSelection() != null || getCurrentEvent().data != null; } }