diff options
author | Patrick Tasse | 2017-05-01 20:04:39 +0000 |
---|---|---|
committer | Patrick Tasse | 2017-05-08 18:41:45 +0000 |
commit | 1dea564fc9022376de284109bc2ca1cfb9ae82fe (patch) | |
tree | bcd1066e092b95e30496207c74b5d27953aabed8 | |
parent | f5bab67728a5cdf5e3094ea34b01a610330d3a74 (diff) | |
download | org.eclipse.swtbot-1dea564fc9022376de284109bc2ca1cfb9ae82fe.tar.gz org.eclipse.swtbot-1dea564fc9022376de284109bc2ca1cfb9ae82fe.tar.xz org.eclipse.swtbot-1dea564fc9022376de284109bc2ca1cfb9ae82fe.zip |
Bug 516017: Implement Drag and Drop using DNDEvent
Change-Id: I8f5224ced51bb11989b14d1886c1dae562ce65e8
Signed-off-by: Patrick Tasse <patrick.tasse@gmail.com>
7 files changed, 310 insertions, 64 deletions
diff --git a/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/widgets/DnDTreeTest.java b/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/widgets/DnDTreeTest.java index d93ad624..ea821fc6 100644 --- a/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/widgets/DnDTreeTest.java +++ b/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/widgets/DnDTreeTest.java @@ -1,23 +1,30 @@ +/*******************************************************************************
+ * Copyright (c) 2013, 2017 Rohit Agrawal 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:
+ * Rohit Agrawal - initial API and implementation
+ *******************************************************************************/
package org.eclipse.swtbot.swt.finder.widgets;
import org.eclipse.swt.examples.dnd.DNDExample;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swtbot.swt.finder.test.AbstractSWTShellTest;
import org.junit.Assert;
-import org.junit.Assume;
import org.junit.Test;
public class DnDTreeTest extends AbstractSWTShellTest {
@Override
protected void createUI(Composite parent) {
- // TODO Auto-generated method stub
new DNDExample().open(shell);
}
@Test
public void dragsAndDropsFromTreeToTree() throws Exception {
- Assume.assumeFalse("Drag and drop does not work with Xvnc", isUsingXvnc());
bot.comboBoxInGroup("Widget", 0).setSelection("Tree");
bot.comboBoxInGroup("Widget", 1).setSelection("Tree");
final SWTBotTree sourceTree = bot.tree(0);
@@ -32,26 +39,7 @@ public class DnDTreeTest extends AbstractSWTShellTest { Assert.fail("Drag Source item should have disappeared from source tree");
}
}
- SWTBotTreeItem droppedNote = targetItem.getNode("Drag Source name 0");
- Assert.assertNotNull("Could not find dropped node", droppedNote);
+ SWTBotTreeItem droppedNode = targetItem.expand().getNode("Drag Source name 0");
+ Assert.assertNotNull("Could not find dropped node", droppedNode);
}
-
- public static boolean isUsingXvnc() throws Exception {
- String os = System.getProperty("os.name").toLowerCase();
- if (os.indexOf("nix") < 0 && os.indexOf("nux") < 0 && os.indexOf("aix") < 0) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- return false;
- }
- String xdisplay = System.getenv("DISPLAY"); //$NON-NLS-1$
- // command is like pgrep -l -f "Xvnc.*:0"
- StringBuilder commandBuilder = new StringBuilder();
- commandBuilder.append("/usr/bin/pgrep -l -f "); //$NON-NLS-1$
- commandBuilder.append("X.*"); //$NON-NLS-1$
- commandBuilder.append(xdisplay);
- Process proc = Runtime.getRuntime().exec(commandBuilder.toString());
- proc.waitFor();
- // If pgrep found something, it will return 0;
- return proc.exitValue() == 0;
- }
-
-
}
diff --git a/org.eclipse.swtbot.swt.finder/META-INF/MANIFEST.MF b/org.eclipse.swtbot.swt.finder/META-INF/MANIFEST.MF index d5eb93cb..d585d462 100644 --- a/org.eclipse.swtbot.swt.finder/META-INF/MANIFEST.MF +++ b/org.eclipse.swtbot.swt.finder/META-INF/MANIFEST.MF @@ -26,6 +26,7 @@ Import-Package: junit.framework;version="4.12.0", org.eclipse.swt, org.eclipse.swt.browser, org.eclipse.swt.custom, + org.eclipse.swt.dnd, org.eclipse.swt.events, org.eclipse.swt.graphics, org.eclipse.swt.widgets, diff --git a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/utils/SWTBotEvents.java b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/utils/SWTBotEvents.java index 2b173e6f..93e4abb6 100644 --- a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/utils/SWTBotEvents.java +++ b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/utils/SWTBotEvents.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 Ketan Padegaonkar and others. + * Copyright (c) 2008, 2017 Ketan Padegaonkar 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 @@ -13,6 +13,7 @@ package org.eclipse.swtbot.swt.finder.utils; import java.util.Map.Entry; import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; import org.eclipse.swt.events.ArmEvent; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; @@ -82,6 +83,15 @@ public abstract class SWTBotEvents { EVENTS.put("Show", SWT.Show); //$NON-NLS-1$ EVENTS.put("Traverse", SWT.Traverse); //$NON-NLS-1$ EVENTS.put("Verify", SWT.Verify); //$NON-NLS-1$ + EVENTS.put("DND.DragEnd", DND.DragEnd); //$NON-NLS-1$ + EVENTS.put("DND.DragSetData", DND.DragSetData); //$NON-NLS-1$ + EVENTS.put("DND.DragEnter", DND.DragEnter); //$NON-NLS-1$ + EVENTS.put("DND.DragLeave", DND.DragLeave); //$NON-NLS-1$ + EVENTS.put("DND.DragOver", DND.DragOver); //$NON-NLS-1$ + EVENTS.put("DND.DragOperationChanged", DND.DragOperationChanged); //$NON-NLS-1$ + EVENTS.put("DND.Drop", DND.Drop); //$NON-NLS-1$ + EVENTS.put("DND.DropAccept", DND.DropAccept); //$NON-NLS-1$ + EVENTS.put("DND.DragStart", DND.DragStart); //$NON-NLS-1$ } /** diff --git a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBot.java b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBot.java index cd920b5c..02d46163 100644 --- a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBot.java +++ b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBot.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2016 Ketan Padegaonkar and others. + * Copyright (c) 2008, 2017 Ketan Padegaonkar 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 @@ -9,27 +9,34 @@ * Ketan Padegaonkar - initial API and implementation * Lorenzo Bettini - (Bug 426869) mark new methods with since annotation * Patrick Tasse - Improve SWTBot menu API and implementation (Bug 479091) + * - Implement Drag and Drop using DNDEvent (Bug 516017) * Aparna Argade(Cadence Design Systems, Inc.) - Bug 489179 *******************************************************************************/ package org.eclipse.swtbot.swt.finder.widgets; import static org.eclipse.swtbot.swt.finder.waits.Conditions.widgetIsEnabled; -import java.awt.AWTException; -import java.awt.Robot; -import java.awt.event.InputEvent; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.util.Geometry; import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSource; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.dnd.TransferData; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Widget; import org.eclipse.swtbot.swt.finder.SWTBot; @@ -931,54 +938,231 @@ public abstract class AbstractSWTBot<T extends Widget> { keyboard().pressShortcut(keys); return this; } - + + /** + * Returns the control used for Drag and Drop operations. + * + * @return the control + * @since 2.6 + */ + protected Control getDNDControl() { + throw new UnsupportedOperationException("This operation is not supported by this widget."); + } + + /** + * Start a drag operation, by making sure the source control has focus and + * the source widget is selected, if applicable. + * + * @since 2.6 + */ + protected void dragStart() { + throw new UnsupportedOperationException("This operation is not supported by this widget."); + } + /** * @since 2.2 */ public void dragAndDrop(final AbstractSWTBot<? extends Widget> target) { - final Rectangle sourceRect = absoluteLocation(); - final Rectangle destRect = target.absoluteLocation(); - dragAndDropPointToPoint(Geometry.centerPoint(sourceRect), Geometry.centerPoint(destRect)); - } - - private void dragAndDropPointToPoint(final Point source, final Point dest) { - try { - final Robot robot = new Robot(); - syncExec(new VoidResult() { - public void run() { - robot.mouseMove(source.x, source.y); - robot.mousePress(InputEvent.BUTTON1_MASK); - robot.mouseMove((source.x + 10), source.y); - } - }); - waitForIdle(robot); + DragSource dragSource = syncExec(new Result<DragSource>() { + public DragSource run() { + Control control = getDNDControl(); + return control == null ? null : (DragSource) control.getData(DND.DRAG_SOURCE_KEY); + } + }); + if (dragSource == null) { + log.error(MessageFormat.format("Widget {0} is not configured for drag support.", this)); + return; + } - syncExec(new VoidResult() { - public void run() { - robot.mouseMove((dest.x + 10), dest.y); - robot.mouseMove(dest.x, dest.y); - } - }); + DropTarget dropTarget = syncExec(new Result<DropTarget>() { + public DropTarget run() { + Control control = target.getDNDControl(); + return control == null ? null : (DropTarget) control.getData(DND.DROP_TARGET_KEY); + } + }); + if (dropTarget == null) { + log.error(MessageFormat.format("Widget {0} is not configured for drop support.", target)); + return; + } - waitForIdle(robot); + dragStart(); - syncExec(new VoidResult() { - public void run() { - robot.mouseRelease(InputEvent.BUTTON1_MASK); + // SWT.DragDetect + Event dragDetectEvent = createMouseEvent(1, SWT.NONE, 0); + notifyDragDetect(dragDetectEvent); + + // DND.DragStart -> DragSource + Event dragStartEvent = createDNDEvent(); + dragStartEvent.x = dragDetectEvent.x; + dragStartEvent.y = dragDetectEvent.y; + notify(DND.DragStart, dragStartEvent, dragSource); + if (!dragStartEvent.doit) { + return; + } + + List<TransferData> dataTypeList = new ArrayList<TransferData>(); + for (Transfer sourceTransfer : dragSource.getTransfer()) { + for (TransferData sourceDataType : sourceTransfer.getSupportedTypes()) { + for (Transfer targetTransfer : dropTarget.getTransfer()) { + if (targetTransfer.isSupportedType(sourceDataType)) { + dataTypeList.add(sourceDataType); + break; + } } - }); - waitForIdle(robot); - } catch (final AWTException e) { - log.error(e.getMessage(), e); - throw new RuntimeException(e); + } + } + TransferData[] dataTypes = dataTypeList.toArray(new TransferData[dataTypeList.size()]); + if (dataTypes.length == 0) { + // DND.DragEnd -> DragSource + Event dragEndEvent = createDNDEvent(); + dragEndEvent.doit = false; + notify(DND.DragEnd, dragEndEvent, dragSource); + return; + } + + // DND.DragEnter -> DropTarget + Event dragEnterEvent = createDNDEvent(); + Point targetAbsoluteLocation = Geometry.centerPoint(target.absoluteLocation()); + dragEnterEvent.x = targetAbsoluteLocation.x; + dragEnterEvent.y = targetAbsoluteLocation.y; + setDNDEventField(dragEnterEvent, "dataType", dataTypes[0]); + setDNDEventField(dragEnterEvent, "dataTypes", dataTypes); + setDNDEventField(dragEnterEvent, "operations", DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK); + setDNDEventField(dragEnterEvent, "feedback", DND.FEEDBACK_SELECT); + dragEnterEvent.detail = DND.DROP_MOVE; + dragEnterEvent.item = target.widget; + notify(DND.DragEnter, dragEnterEvent, dropTarget); + + // DND.DragOver -> DropTarget + Event dragOverEvent = createDNDEvent(dragEnterEvent); + notify(DND.DragOver, dragOverEvent, dropTarget); + + // DND.DragLeave -> DropTarget + Event dragLeaveEvent = createDNDEvent(); + dragLeaveEvent.item = target.widget; + notify(DND.DragLeave, dragLeaveEvent, dropTarget); + + // DND.DropAccept -> DropTarget + Event dropAcceptEvent = createDNDEvent(dragOverEvent); + notify(DND.DropAccept, dropAcceptEvent, dropTarget); + if (dropAcceptEvent.detail == DND.DROP_NONE) { + // DND.DragEnd -> DragSource + Event dragEndEvent = createDNDEvent(); + dragEndEvent.doit = false; + notify(DND.DragEnd, dragEndEvent, dragSource); + return; + } + + // DND.DragSetData -> DragSource + Event dragSetDataEvent = createDNDEvent(); + setDNDEventField(dragSetDataEvent, "dataType", getDNDEventField(dropAcceptEvent, "dataType")); + notify(DND.DragSetData, dragSetDataEvent, dragSource); + if (!dragSetDataEvent.doit) { + // DND.DragEnd -> DragSource + Event dragEndEvent = createDNDEvent(); + dragEndEvent.doit = false; + notify(DND.DragEnd, dragEndEvent, dragSource); + return; + } + + // DND.Drop -> DropTarget + Event dropEvent = createDNDEvent(dropAcceptEvent); + dropEvent.data = dragSetDataEvent.data; + notify(DND.Drop, dropEvent, dropTarget); + + // DND.DragEnd -> DragSource + Event dragEndEvent = createDNDEvent(); + dragEndEvent.detail = dropEvent.detail; + dragEndEvent.doit = dragEndEvent.detail != DND.DROP_NONE; + notify(DND.DragEnd, dragEndEvent, dragSource); + } + + private Event createDNDEvent() { + try { + Class<?> clazz = Class.forName("org.eclipse.swt.dnd.DNDEvent"); + Constructor<?> constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + Event dndEvent = (Event) constructor.newInstance(); + dndEvent.time = (int) System.currentTimeMillis(); + dndEvent.widget = widget; + dndEvent.display = display; + return dndEvent; + } catch (ReflectiveOperationException e) { + throw new UnsupportedOperationException(e); } + } + + private Event createDNDEvent(Event other) { + Event dndEvent = createDNDEvent(); + dndEvent.x = other.x; + dndEvent.y = other.y; + dndEvent.item = other.item; + dndEvent.detail = other.detail; + setDNDEventField(dndEvent, "dataType", getDNDEventField(other, "dataType")); + setDNDEventField(dndEvent, "dataTypes", getDNDEventField(other, "dataTypes")); + setDNDEventField(dndEvent, "operations", getDNDEventField(other, "operations")); + setDNDEventField(dndEvent, "feedback", DND.FEEDBACK_SELECT); + setDNDEventField(dndEvent, "image", getDNDEventField(other, "image")); + setDNDEventField(dndEvent, "offsetX", getDNDEventField(other, "offsetX")); + setDNDEventField(dndEvent, "offsetY", getDNDEventField(other, "offsetY")); + return dndEvent; + } + + private void setDNDEventField(Event dndEvent, String fieldName, Object value) { + try { + Class<?> clazz = Class.forName("org.eclipse.swt.dnd.DNDEvent"); + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(dndEvent, value); + } catch (ReflectiveOperationException e) { + throw new UnsupportedOperationException(e); + } + } + private Object getDNDEventField(Event dndEvent, String fieldName) { + try { + Class<?> clazz = Class.forName("org.eclipse.swt.dnd.DNDEvent"); + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(dndEvent); + } catch (ReflectiveOperationException e) { + throw new UnsupportedOperationException(e); + } } - private void waitForIdle(final Robot robot) { - if (SWT.getPlatform().equals("gtk")) { - robot.waitForIdle(); + private void notifyDragDetect(Event dragDetectEvent) { + // Don't send SWT.DragDetect to the DragSource listener, + // otherwise the cursor gets stuck in drag mode + final Control control = getDNDControl(); + final Listener dragSourceListener = syncExec(new Result<Listener>() { + public Listener run() { + // The DragSource listener is an anonymous class of DragSource + for (Listener listener : control.getListeners(SWT.DragDetect)) { + if (DragSource.class.equals(listener.getClass().getEnclosingClass())) { + return listener; + } + } + return null; + } + }); + try { + if (dragSourceListener != null) { + syncExec(new VoidResult() { + public void run() { + control.removeListener(SWT.DragDetect, dragSourceListener); + } + }); + } + notify(SWT.DragDetect, dragDetectEvent, control); + } finally { + if (dragSourceListener != null) { + syncExec(new VoidResult() { + public void run() { + control.addListener(SWT.DragDetect, dragSourceListener); + } + }); + } } } } diff --git a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBotControl.java b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBotControl.java index dc85455a..19daa889 100644 --- a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBotControl.java +++ b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/AbstractSWTBotControl.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.swtbot.swt.finder.widgets; +import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; @@ -56,6 +57,19 @@ public class AbstractSWTBotControl<T extends Control> extends AbstractSWTBot<T> }); } + @Override + protected Control getDNDControl() { + return widget; + } + + @Override + protected void dragStart() { + setFocus(); + notify(SWT.Activate); + notify(SWT.FocusIn); + notify(SWT.MouseDown, createMouseEvent(1, SWT.NONE, 1)); + } + /** * Get the bounds of the Widget in relation to Display * diff --git a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTableItem.java b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTableItem.java index 32bbb138..bda355de 100644 --- a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTableItem.java +++ b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTableItem.java @@ -17,6 +17,7 @@ package org.eclipse.swtbot.swt.finder.widgets; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; @@ -183,6 +184,25 @@ public class SWTBotTableItem extends AbstractSWTBot<TableItem> { } @Override + protected Control getDNDControl() { + return table; + } + + @Override + protected void dragStart() { + syncExec(new VoidResult() { + public void run() { + table.setFocus(); + table.setSelection(widget); + } + }); + notifyTable(SWT.Activate); + notifyTable(SWT.FocusIn); + notifyTable(SWT.MouseDown, createMouseEvent(1, SWT.NONE, 1)); + notifyTable(SWT.Selection, createSelectionEvent(SWT.BUTTON1)); + } + + @Override protected Rectangle getBounds() { return syncExec(new Result<Rectangle>() { public Rectangle run() { @@ -373,4 +393,12 @@ public class SWTBotTableItem extends AbstractSWTBot<TableItem> { }); } + @Override + protected Rectangle absoluteLocation() { + return syncExec(new Result<Rectangle>() { + public Rectangle run() { + return display.map(widget.getParent(), null, widget.getBounds()); + } + }); + } } diff --git a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTreeItem.java b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTreeItem.java index e682ea0f..3d49918a 100644 --- a/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTreeItem.java +++ b/org.eclipse.swtbot.swt.finder/src/org/eclipse/swtbot/swt/finder/widgets/SWTBotTreeItem.java @@ -25,6 +25,7 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; @@ -473,6 +474,26 @@ public class SWTBotTreeItem extends AbstractSWTBot<TreeItem> { } @Override + protected Control getDNDControl() { + return tree; + } + + @Override + protected void dragStart() { + syncExec(new VoidResult() { + public void run() { + tree.setFocus(); + tree.setSelection(widget); + lastSelectionItem = widget; + } + }); + notifyTree(SWT.Activate); + notifyTree(SWT.FocusIn); + notifyTree(SWT.MouseDown, createMouseEvent(1, SWT.NONE, 1)); + notifyTree(SWT.Selection, createSelectionEvent(SWT.BUTTON1)); + } + + @Override protected Rectangle getBounds() { return syncExec(new Result<Rectangle>() { public Rectangle run() { |