blob: 6d6d56678b4efe9f63c611b32c5e6bebab3face2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Paul Pazderski and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Paul Pazderski - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.tests.win32;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.ByteArrayTransfer;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.HTMLTransfer;
import org.eclipse.swt.dnd.ImageTransfer;
import org.eclipse.swt.dnd.RTFTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.URLTransfer;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Some simple tests for drag and drop.
* <p>
* To test drag and drop the appropriate user input must be simulated. This
* requires that the test window is visible/active/focused and if it fails may
* move something or trigger other actions when the input is simulated but the
* test window not at the expected position. This should be no problem for fully
* automated tests but may disturb when run locally in background as part of a
* larger test suite.
* </p>
*/
public class Test_org_eclipse_swt_dnd_DND {
private Shell shell;
@Before
public void setUp() {
shell = new Shell();
shell.setLayout(new RowLayout());
shell.setSize(300, 300);
shell.open();
try {
SwtWin32TestUtil.processEvents(shell.getDisplay(), 1000, shell::isVisible);
} catch (InterruptedException e) {
fail("Initialization interrupted");
}
assertTrue("Shell not visible.", shell.isVisible());
}
@After
public void tearDown() {
Display display = shell.getDisplay();
display.dispose();
assertTrue(display.isDisposed());
}
/**
* DnD some random bytes using a custom transfer type and the abstract {@link ByteArrayTransfer}.
*/
@Test
public void testByteArrayTransfer() throws InterruptedException {
final byte[] drag = new byte[1024];
final byte[] drop;
new Random(4).nextBytes(drag);
drop = testTransferRoundtrip(new ByteArrayTransfer() {
private final String TEST_TYPE = "SWT JUnit Transfer Test";
private final int TEST_ID = registerType(TEST_TYPE);
@Override
protected String[] getTypeNames() {
return new String[] { TEST_TYPE };
}
@Override
protected int[] getTypeIds() {
return new int[] { TEST_ID };
}
}, drag);
assertArrayEquals("Drop received other data as we dragged.", drag, drop);
}
/**
* DnD some filenames using the {@link FileTransfer}.
*/
@Test
public void testFileTransfer() throws InterruptedException {
final String[] drag = new String[] { "C:\\temp\\file1", "C:\\temp\\file2" };
final String[] drop;
drop = testTransferRoundtrip(FileTransfer.getInstance(), drag);
assertArrayEquals("Drop received other data as we dragged.", drag, drop);
}
/**
* DnD some HTML using the {@link HTMLTransfer}.
*/
@Test
public void testHtmlTransfer() throws InterruptedException {
final String drag = "<p>This is a paragraph of text.</p>";
final String drop;
drop = testTransferRoundtrip(HTMLTransfer.getInstance(), drag);
assertEquals("Drop received other data as we dragged.", drag, drop);
}
/**
* DnD an image using the {@link ImageTransfer}.
*/
@Test
public void testImageTransfer() throws InterruptedException {
final Image image = new Image(shell.getDisplay(), 16, 16);
try {
Color color = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE);
GC gc = new GC(image);
gc.setBackground(color);
gc.fillRectangle(image.getBounds());
gc.dispose();
final ImageData drag = image.getImageData();
final ImageData drop;
drop = testTransferRoundtrip(ImageTransfer.getInstance(), drag);
// ImageData has no custom equals method and the default one isn't sufficient
boolean equals = (drag == drop);
if (!equals && drag != null && drop != null) {
equals = (drag.width == drop.width && drag.height == drop.height);
if (equals) {
outer: for (int y = 0; y < drag.height; y++) {
for (int x = 0; x < drag.width; x++) {
if (drag.getPixel(x, y) != drop.getPixel(x, y)) {
equals = false;
break outer;
}
}
}
}
}
assertTrue("Drop received other data as we dragged.", equals);
} finally {
image.dispose();
}
}
/**
* DnD some formatted text using the {@link RTFTransfer}.
*/
@Test
public void testRtfTransfer() throws InterruptedException {
final String drag = "{\\rtf1{\\colortbl;\\red255\\green0\\blue0;}\\uc1\\b\\i Hello World}";
final String drop;
drop = testTransferRoundtrip(RTFTransfer.getInstance(), drag);
assertEquals("Drop received other data as we dragged.", drag, drop);
}
/**
* DnD some simple text using the {@link TextTransfer}.
*/
@Test
public void testTextTransfer() throws InterruptedException {
final String drag = "Hello World";
final String drop;
drop = testTransferRoundtrip(TextTransfer.getInstance(), drag);
assertEquals("Drop received other data as we dragged.", drag, drop);
}
/**
* DnD an URL using the {@link URLTransfer}.
*/
@Test
public void testUrlTransfer() throws InterruptedException {
final String drag = "https://www.eclipse.org";
final String drop;
drop = testTransferRoundtrip(URLTransfer.getInstance(), drag);
assertEquals("Drop received other data as we dragged.", drag, drop);
}
/**
* Test transfer implementation by dragging and dropping some data onto ourself.
*
* @param <T> the data's type
* @param transfer the transfer type to test
* @param data the data to drag and drop
* @return the data received from the drop
*/
private <T> T testTransferRoundtrip(Transfer transfer, T data) throws InterruptedException {
int maxTries = 3;
AtomicBoolean dropped = new AtomicBoolean(false);
AtomicReference<T> droppedData = new AtomicReference<>(null);
DragSource source = new DragSource(shell, DND.DROP_LINK | DND.DROP_COPY | DND.DROP_MOVE);
source.setTransfer(transfer);
source.addListener(DND.DragSetData, event -> event.data = data);
DropTarget target = new DropTarget(shell, DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_MOVE);
target.setTransfer(transfer);
target.addListener(DND.Drop, event -> {
try {
@SuppressWarnings("unchecked")
T o = (T) event.data;
droppedData.set(o);
} catch (ClassCastException ex) {
}
dropped.set(true);
});
do {
postDragAndDropEvents();
SwtWin32TestUtil.processEvents(shell.getDisplay(), 2000, dropped::get);
} while(!dropped.get() && --maxTries > 0);
assertTrue("No drop received.", dropped.get());
assertNotNull("No data was dropped.", droppedData.get());
return droppedData.get();
}
/**
* Posts the events required to do a drag and drop. (i.e. click and hold mouse button and move mouse)
* <p>
* The caller is responsible to ensure the generated events are processed by the event loop.
* </p>
*/
private void postDragAndDropEvents() {
shell.forceActive();
assertTrue("Test shell requires input focus.", shell.forceFocus());
Event event = new Event();
Point pt = shell.toDisplay(50, 50);
event.x = pt.x;
event.y = pt.y;
event.type = SWT.MouseMove;
shell.getDisplay().post(event);
event.button = 1;
event.count = 1;
event.type = SWT.MouseDown;
shell.getDisplay().post(event);
event.x += 30;
event.type = SWT.MouseMove;
shell.getDisplay().post(event);
event.type = SWT.MouseUp;
shell.getDisplay().post(event);
}
}