/******************************************************************************* * Copyright (c) 2000, 2007 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.wst.jsdt.internal.ui.dnd; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.DropTargetListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.TreeItem; /** * A drag and drop adapter to be used together with structured viewers. * The adapater delegates the <code>dragEnter</code>, <code>dragOperationChanged * </code>, <code>dragOver</code> and <code>dropAccept</code> method to the * <code>validateDrop</code> method. Furthermore it adds location feedback. */ public class JdtViewerDropAdapter implements DropTargetListener { /** * Constant describing the position of the mouse cursor relative * to the target object. This means the mouse is not positioned * over or near any valid target. */ public static final int LOCATION_NONE= DND.FEEDBACK_NONE; /** * Constant describing the position of the mouse cursor relative * to the target object. This means the mouse is positioned * directly on the target. */ public static final int LOCATION_ON= DND.FEEDBACK_SELECT; /** * Constant describing the position of the mouse cursor relative * to the target object. This means the mouse is positioned * slightly before the target. */ public static final int LOCATION_BEFORE= DND.FEEDBACK_INSERT_BEFORE; /** * Constant describing the position of the mouse cursor relative * to the target object. This means the mouse is positioned * slightly after the target. */ public static final int LOCATION_AFTER= DND.FEEDBACK_INSERT_AFTER; /** * The threshold used to determine if the mouse is before or after * an item. */ private static final int LOCATION_EPSILON= 5; private static final int ITEM_MARGIN_LEFT= 40; private static final int ITEM_MARGIN_RIGTH= 10; /** * Style to enable location feedback. */ public static final int INSERTION_FEEDBACK= 1 << 1; private StructuredViewer fViewer; private int fFeedback; private boolean fShowInsertionFeedback; private boolean fFullWidthMatchesItem; private int fRequestedOperation; private int fLastOperation; protected int fLocation; protected Object fTarget; public JdtViewerDropAdapter(StructuredViewer viewer, int feedback) { Assert.isNotNull(viewer); fViewer= viewer; fFeedback= feedback; fLastOperation= -1; fFullWidthMatchesItem= true; } /** * Controls whether the drop adapter shows insertion feedback or not. * * @param showInsertionFeedback <code>true</code> if the drop adapter is supposed * to show insertion feedback. Otherwise <code>false</code> */ public void showInsertionFeedback(boolean showInsertionFeedback) { fShowInsertionFeedback= showInsertionFeedback; } /** * Controls whether the drop adapter matches an tree or table item on the full width of the viewer. * * @param enable <code>true</code> if the drop adapter is supposed * to test the horizontal distance */ protected void setFullWidthMatchesItem(boolean enable) { fFullWidthMatchesItem= enable; } /** * Returns the viewer this adapter is working on. */ protected StructuredViewer getViewer() { return fViewer; } //---- Hooks to override ----------------------------------------------------- /** * The actual drop has occurred. Calls <code>drop(Object target, DropTargetEvent event) * </code>. * @see DropTargetListener#drop(org.eclipse.swt.dnd.DropTargetEvent) */ public void drop(DropTargetEvent event) { drop(fTarget, event); } /** * The actual drop has occurred. * @param target the drop target in form of a domain element. * @param event the drop traget event */ public void drop(Object target, DropTargetEvent event) { } /** * Checks if the drop is valid. The method calls <code>validateDrop * (Object target, DropTargetEvent event). Implementors can alter the * <code>currentDataType</code> field and the <code>detail</code> field * to give feedback about drop acceptence. */ public void validateDrop(DropTargetEvent event) { validateDrop(fTarget, event, fRequestedOperation); } /** * Checks if the drop on the current target is valid. The method * can alter the <code>currentDataType</code> field and the <code> * detail</code> field to give feedback about drop acceptence. * @param target the drop target in form of a domain element. * @param event the drop traget event * @param operation the operation requested by the user. */ public void validateDrop(Object target, DropTargetEvent event, int operation) { } public void dragEnter(DropTargetEvent event) { dragOperationChanged(event); } public void dragLeave(DropTargetEvent event) { fTarget= null; fLocation= LOCATION_NONE; } public void dragOperationChanged(DropTargetEvent event) { fRequestedOperation= event.detail; fTarget= computeTarget(event); fLocation= computeLocation(event); validateDrop(event); fLastOperation= event.detail; computeFeedback(event); } public void dragOver(DropTargetEvent event) { Object oldTarget= fTarget; fTarget= computeTarget(event); //set the location feedback int oldLocation= fLocation; fLocation= computeLocation(event); if (oldLocation != fLocation || oldTarget != fTarget || fLastOperation != event.detail) { validateDrop(event); fLastOperation= event.detail; } else { event.detail= fLastOperation; } computeFeedback(event); } public void dropAccept(DropTargetEvent event) { fTarget= computeTarget(event); validateDrop(event); fLastOperation= event.detail; } /** * Returns the data held by <code>event.item</code>. Inside a viewer * this corresponds to the items data model element. */ protected Object computeTarget(DropTargetEvent event) { if (event.item == null) { return null; } if (!fFullWidthMatchesItem) { Point coordinates= fViewer.getControl().toControl(new Point(event.x, event.y)); Rectangle bounds= getBounds((Item) event.item); if (coordinates.x < bounds.x - ITEM_MARGIN_LEFT || coordinates.x >= bounds.x + bounds.width + ITEM_MARGIN_RIGTH) { event.item= null; // too far away return null; } } return event.item.getData(); } /** * Returns the position of the given coordinates relative to the given target. * The position is determined to be before, after, or on the item, based on * some threshold value. The return value is one of the LOCATION_* constants * defined in this class. */ protected int computeLocation(DropTargetEvent event) { if (!(event.item instanceof Item)) return LOCATION_NONE; Item item= (Item) event.item; Point coordinates= fViewer.getControl().toControl(new Point(event.x, event.y)); Rectangle bounds= getBounds(item); if (bounds == null) { return LOCATION_NONE; } if ((coordinates.y - bounds.y) < LOCATION_EPSILON) { return LOCATION_BEFORE; } if ((bounds.y + bounds.height - coordinates.y) < LOCATION_EPSILON) { return LOCATION_AFTER; } return LOCATION_ON; } /** * Returns the bounds of the given item, or <code>null</code> if it is not a * valid type of item. */ private Rectangle getBounds(Item item) { if (item instanceof TreeItem) return ((TreeItem) item).getBounds(); if (item instanceof TableItem) return ((TableItem) item).getBounds(0); return null; } /** * Sets the drag under feedback corresponding to the value of <code>fLocation</code> * and the <code>INSERTION_FEEDBACK</code> style bit. */ protected void computeFeedback(DropTargetEvent event) { if (!fShowInsertionFeedback && fLocation != LOCATION_NONE) { event.feedback= DND.FEEDBACK_SELECT; } else { event.feedback= fLocation; } event.feedback|= fFeedback; } /** * Sets the drop operation to </code>DROP_NODE<code>. */ protected void clearDropOperation(DropTargetEvent event) { event.detail= DND.DROP_NONE; } /** * Returns the requested drop operation. */ protected int getRequestedOperation() { return fRequestedOperation; } protected void setDefaultFeedback(int feedback) { fFeedback= feedback; } //---- helper methods to test DnD public void internalTestSetLocation(int location) { fLocation= location; } }