blob: ecf72047256f132733b1b30f1eeb398f33cfd12e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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.jdt.internal.ui.dnd;
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;
import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.StructuredViewer;
/**
* 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;
}
}