blob: b646cac0cbe360b03437fdc97f4404aed2a9be77 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 Obeo 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:
* Mariot Chauvin <mariot.chauvin@obeo.fr> - initial API and implementation
*******************************************************************************/
package org.eclipse.platform.discovery.ui.test.comp.internal.pageobjects.swtbot.utils;
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.InputEvent;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
import org.eclipse.swtbot.swt.finder.results.VoidResult;
import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBot;
/**
* A dnd utility class to help performing drag and drop. This code will be in
* SWTbot in future but need to be polished before.
*
* @author mchauvin
*/
/*
* FIXME
* This is copy-pasted from https://bugs.eclipse.org/bugs/show_bug.cgi?id=285271
* Once this makes it in the SWTBot builds, we need to delete this class
*/
public class DndUtil {
/**
* Number of pixels traversed before a drag starts. Ms : 10 OSX : 10(1.3.1),
* 5(1.4.1) Linux/X11: delay+16
*/
private static final int DRAG_THRESHOLD = "gtk".equals(SWT.getPlatform()) ? 16 : 10;
/**
* delay before moving after mouse is pressed. could be used for delay after
* moving before mouse is released
*/
private static final int DRAG_DELAY = 400;
private Display display;
/**
* Construct a new instance.
*
* @param display
* the display to use
*/
public DndUtil(Display display) {
this.display = display;
}
/**
* Performs a drag and drop operation from this widget to the given target.
* The drag start location will be chosen depending on this widget's default
* implementation.
*
* @param source
* the source widget to drag
* @param target
* To perform the drop on
* @see #dragAndDrop(Point)
*/
// TODO add modifier key mask
public void dragAndDrop(final AbstractSWTBot<? extends Widget> source, final AbstractSWTBot<? extends Widget> target) {
dragAndDrop(source, on(target));
}
/**
* Performs a drag and drop operation from this widget to the given target
* at the given location from target origin. The drag start location will be
* chosen depending on this widget's default implementation.
*
* @param source
* the source widget to drag
* @param target
* To perform the drop on
* @param locationOnTarget
* The target locations, from target origin, where the DND shall
* finish.
* @see #dragAndDrop(Point)
*/
public void dragAndDrop(final AbstractSWTBot<? extends Widget> source, final AbstractSWTBot<? extends Widget> target, final Point locationOnTarget) {
Rectangle targetRectangle = absoluteLocation(target);
Point dropTarget = new Point(targetRectangle.x + locationOnTarget.x, targetRectangle.y + locationOnTarget.y);
dragAndDrop(source, dropTarget);
}
/**
* Performs a DND operation to an arbitrary location. The drag start
* location will be chosen depending on this widget's default
* implementation.
*
* @param source
* the source widget to drag
* @param target
* The target locations where the DND shall finish.
* @see #before(AbstractSWTBot)
* @see #on(AbstractSWTBot)
* @see #after(AbstractSWTBot)
*/
public void dragAndDrop(final AbstractSWTBot<? extends Widget> source, final Point target) {
final Rectangle sourceLocation = absoluteLocation(source);
final Point slightOffset = Geometry.add(Geometry.getLocation(sourceLocation), new Point(DRAG_THRESHOLD, DRAG_THRESHOLD));
doDragAndDrop(Geometry.min(Geometry.centerPoint(sourceLocation), slightOffset), target);
}
/**
* Performs a DND operation starting at an arbitrary location, targeting the
* given widget.
*
* @param source
* From where to start dragging
* @param target
* Where to drop onto
* @see #dragAndDrop(AbstractSWTBot)
* @see #dragAndDrop(Point)
*/
protected void dragAndDrop(final Point source, final AbstractSWTBot<? extends Widget> target) {
doDragAndDrop(source, on(target));
}
/**
*
*/
private void doDragAndDrop(final Point source, final Point dest) {
// log.debug(MessageFormat.format("Drag-and-dropping from ({0},{1}) to ({2},{3})",
// source.x, source.y, dest.x, dest.y));
try {
final Robot awtRobot = new Robot();
// the x+10 motion is needed to let native functions register a drag
// detect. It did not work under Windows
// otherwise and has been reported to be required for linux, too.
// But I could not test that.
syncExec(new VoidResult() {
public void run() {
awtRobot.mouseMove(source.x, source.y);
awtRobot.mousePress(InputEvent.BUTTON1_MASK);
awtRobot.mouseMove(source.x + DRAG_THRESHOLD, source.y);
}
});
/* drag delay */
SWTUtils.sleep(DRAG_DELAY);
syncExec(new VoidResult() {
public void run() {
awtRobot.mouseMove(dest.x + DRAG_THRESHOLD, dest.y);
awtRobot.mouseMove(dest.x, dest.y);
}
});
/* drop delay */
SWTUtils.sleep(DRAG_DELAY);
syncExec(new VoidResult() {
public void run() {
awtRobot.mouseRelease(InputEvent.BUTTON1_MASK);
}
});
/* delay while drop listeners take action*/
SWTUtils.sleep(DRAG_DELAY);
} catch (final AWTException e) {
// log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
/**
* Invokes {@link VoidResult#run()} on the UI thread.
*
* @param toExecute
* the object to be invoked in the UI thread.
*/
private void syncExec(VoidResult toExecute) {
UIThreadRunnable.syncExec(display, toExecute);
}
/**
* Calculates a position which can be used to insert an item <em>before</em>
* the given one by a DND operation.
*
* @param targetItem
* Before which the new item shall appear
* @param <T>
* .
* @return A point suitable to drop an item before {@code targetItem}
* @see #dragAndDrop(Point)
* @see DND#FEEDBACK_INSERT_BEFORE
*/
public static <T extends Widget> Point before(final AbstractSWTBot<T> targetItem) {
return pointOnUpperBorder(absoluteLocation(targetItem));
}
/**
* Calculates a position which can be used to drop something <em>onto</em>
* the given widget by a DND operation. For tree structures, this will most
* likely result in another child node being added. But how this is handled
* in detail ultimately depends on the "drop action"'s implementation.
*
* @param targetItem
* On which to drop
* @param <T>
* .
* @return The {@code targetItem}'s center
* @see #dragAndDrop(Point)
* @see DND#FEEDBACK_SELECT
*/
public static <T extends Widget> Point on(final AbstractSWTBot<T> targetItem) {
return Geometry.centerPoint(absoluteLocation(targetItem));
}
/**
* Calculates a position which can be used to insert an item after the given
* one by a DND operation.
*
* @param targetItem
* After which the new item shall appear
* @param <T>
* .
* @return A point suitable to drop an item after {@code targetItem}
* @see #dragAndDrop(Point)
* @see DND#FEEDBACK_INSERT_AFTER
*/
public static <T extends Widget> Point after(final AbstractSWTBot<T> targetItem) {
return pointOnLowerBorder(absoluteLocation(targetItem));
}
private static Point pointOnLowerBorder(Rectangle rect) {
return new Point(Geometry.centerPoint(rect).x, rect.y + rect.height - 1);
}
private static Point pointOnUpperBorder(Rectangle rect) {
return new Point(Geometry.centerPoint(rect).x, rect.y + 1);
}
private static <T extends Widget> Rectangle absoluteLocation(final AbstractSWTBot<T> item) {
return LocationUtils.absoluteLocation(item);
}
}