diff --git a/test/jdk/java/awt/dnd/MissedDragEnterTest.java b/test/jdk/java/awt/dnd/MissedDragEnterTest.java new file mode 100644 index 00000000000..60fc5c3b85f --- /dev/null +++ b/test/jdk/java/awt/dnd/MissedDragEnterTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JFrame; +import javax.swing.JPanel; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceDragEvent; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetContext; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/* + @test + @bug 4388802 + @summary tests that dragEnter() is called on a DropTargetListener if its drop + target is associated with a component which initiated the drag + @key headful + @run main MissedDragEnterTest +*/ + +public class MissedDragEnterTest { + + static final int FRAME_ACTIVATION_TIMEOUT = 1000; + volatile JFrame frame; + volatile DragSourceDropTargetPanel panel; + volatile Point p; + volatile Dimension d; + + public static void main(String[] args) throws Exception { + MissedDragEnterTest test = new MissedDragEnterTest(); + EventQueue.invokeAndWait(test::init); + try { + test.start(); + } finally { + EventQueue.invokeAndWait(() -> { + if (test.frame != null) { + test.frame.dispose(); + } + }); + } + } + + public void init() { + panel = new DragSourceDropTargetPanel(); + frame = new JFrame(); + frame.setTitle("MissedDragEnterTest"); + frame.setLocation(200, 200); + frame.getContentPane().add(panel); + + frame.pack(); + frame.setVisible(true); + } + + public void start() throws Exception { + Robot robot = new Robot(); + + robot.delay(FRAME_ACTIVATION_TIMEOUT); + EventQueue.invokeAndWait(() -> { + p = panel.getLocationOnScreen(); + d = panel.getSize(); + }); + + p.translate(d.width / 2, d.height / 2); + robot.mouseMove(p.x, p.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_MASK); + for (int i = 0; i < d.width; i++) { + p.translate(1, 1); + robot.mouseMove(p.x, p.y); + robot.delay(10); + } + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + + EventQueue.invokeAndWait(() -> { + if (!panel.getResult()) { + throw new RuntimeException("The test failed."); + } + }); + } +} + +class DragSourceDropTargetPanel extends JPanel implements DropTargetListener, + Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + private final DataFlavor dataflavor = + new DataFlavor(JPanel.class, "panel"); + private final Dimension preferredDimension = new Dimension(200, 100); + private boolean inside = false; + private boolean passed = true; + + public DragSourceDropTargetPanel() { + setLayout(new FlowLayout()); + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + setDropTarget(new DropTarget(this, this)); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) {} + + public void dragExit(DragSourceEvent dse) {} + + public void dragOver(DragSourceDragEvent dsde) {} + + public void dragDropEnd(DragSourceDropEvent dsde) {} + + public void dropActionChanged(DragSourceDragEvent dsde) {} + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + Object retObj = null; + + ByteArrayOutputStream baoStream = new ByteArrayOutputStream(); + ObjectOutputStream ooStream = new ObjectOutputStream(baoStream); + ooStream.writeObject(this); + + ByteArrayInputStream baiStream = new ByteArrayInputStream(baoStream.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(baiStream); + try { + retObj = ois.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException(e.toString()); + } + + return retObj; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { dataflavor }; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + + public Dimension getPreferredSize() { + return preferredDimension; + } + + public void dragEnter(DropTargetDragEvent dtde) { + inside = true; + } + + public void dragExit(DropTargetEvent dte) { + if (!inside) { + passed = false; + inside = false; + throw new RuntimeException("dragEnter() is not called before dragExit()"); + + } + inside = false; + } + + public void dragOver(DropTargetDragEvent dtde) { + if (!inside) { + passed = false; + throw new RuntimeException("dragEnter() is not called before dragOver()"); + } + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } + + public void drop(DropTargetDropEvent dtde) { + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component)transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + e.printStackTrace(); + dtc.dropComplete(false); + } + } + dtc.dropComplete(true); + + add(comp); + } + + public boolean getResult() { + return passed; + } +} diff --git a/test/jdk/java/awt/dnd/ModalDialogDeadlockTest.java b/test/jdk/java/awt/dnd/ModalDialogDeadlockTest.java new file mode 100644 index 00000000000..ca72ef8710f --- /dev/null +++ b/test/jdk/java/awt/dnd/ModalDialogDeadlockTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; + +/* + @test + @bug 4633417 + @summary tests that drag operation doesn't cause hang when a modal dialog is + shown + @key headful + @run main ModalDialogDeadlockTest +*/ + +public class ModalDialogDeadlockTest implements AWTEventListener { + + volatile Frame frame; + volatile Dialog dialog; + volatile Point dstPoint; + volatile Point srcPoint; + volatile Dimension d; + + volatile DragSource dragSource; + volatile Transferable transferable; + volatile DragSourceListener dsl; + volatile DragGestureListener dgl; + volatile DragGestureRecognizer dgr; + volatile DropTarget dt; + + static final Object SYNC_LOCK = new Object(); + static final int FRAME_ACTIVATION_TIMEOUT = 2000; + static final int MOUSE_RELEASE_TIMEOUT = 1000; + + Component clickedComponent = null; + + public static void main(String[] args) throws Exception { + ModalDialogDeadlockTest test = new ModalDialogDeadlockTest(); + EventQueue.invokeAndWait(test::init); + try { + test.start(); + } finally { + EventQueue.invokeAndWait(() -> { + if (test.frame != null) { + test.frame.dispose(); + } + }); + } + } + + public void init() { + frame = new Frame("ModalDialogDeadlockTest"); + frame.setBounds(100, 100, 200, 200); + dialog = new Dialog(frame, "Dialog", true); + dialog.setBounds(350, 100, 200, 200); + + dragSource = DragSource.getDefaultDragSource(); + transferable = new StringSelection("TEXT"); + dsl = new DragSourceAdapter() { + public void dragDropEnd(DragSourceDropEvent dsde) { + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + }; + dgl = new DragGestureListener() { + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, transferable, dsl); + } + }; + dgr = dragSource.createDefaultDragGestureRecognizer(dialog, + DnDConstants.ACTION_COPY, + dgl); + final DropTargetListener dtl = new DropTargetAdapter() { + public void drop(DropTargetDropEvent dtde) { + dtde.rejectDrop(); + dialog.dispose(); + } + }; + dt = new DropTarget(frame, dtl); + + frame.getToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.setVisible(true); + } + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void start() throws Exception { + final Robot robot = new Robot(); + + robot.delay(FRAME_ACTIVATION_TIMEOUT); + + EventQueue.invokeAndWait(() -> { + dstPoint = frame.getLocationOnScreen(); + d = frame.getSize(); + }); + dstPoint.translate(d.width / 2, d.height / 2); + + if (!pointInComponent(robot, dstPoint, frame)) { + System.err.println("WARNING: Couldn't locate frame."); + return; + } + + EventQueue.invokeLater(() -> { + dialog.setVisible(true); + }); + + robot.delay(FRAME_ACTIVATION_TIMEOUT); + + EventQueue.invokeAndWait(() -> { + srcPoint = dialog.getLocationOnScreen(); + d = dialog.getSize(); + }); + srcPoint.translate(d.width / 2, d.height / 2); + + if (!pointInComponent(robot, srcPoint, dialog)) { + System.err.println("WARNING: Couldn't locate dialog."); + return; + } + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + for (;!srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.delay(50); + } + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_MASK); + SYNC_LOCK.wait(); + } + } + + public void reset() { + clickedComponent = null; + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component)e.getSource(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + } + + boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + robot.waitForIdle(); + reset(); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_MASK); + SYNC_LOCK.wait(MOUSE_RELEASE_TIMEOUT); + } + + Component c = clickedComponent; + + while (c != null && c != comp) { + c = c.getParent(); + } + + return c == comp; + } +} diff --git a/test/jdk/java/awt/dnd/ModalDialogOnDragDeadlockTest.java b/test/jdk/java/awt/dnd/ModalDialogOnDragDeadlockTest.java new file mode 100644 index 00000000000..3730aec27c9 --- /dev/null +++ b/test/jdk/java/awt/dnd/ModalDialogOnDragDeadlockTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; + +/* + @test + @bug 4352221 + @summary verifies that showing a modal dialog on drag doesn't hang + @key headful + @run main ModalDialogOnDragDeadlockTest +*/ + +public class ModalDialogOnDragDeadlockTest implements AWTEventListener { + + volatile Frame frame; + volatile Dialog dialog; + volatile Point srcPoint; + volatile Dimension d; + volatile boolean finished; + + static final Object SYNC_LOCK = new Object(); + static final int FRAME_ACTIVATION_TIMEOUT = 3000; + static final int DROP_COMPLETION_TIMEOUT = 5000; + static final int MOUSE_RELEASE_TIMEOUT = 1000; + + volatile DragSource dragSource; + volatile Transferable transferable; + volatile DragGestureListener dragGestureListener; + volatile DragGestureRecognizer dragGestureRecognizer; + volatile DropTargetListener dropTargetListener; + volatile DropTarget dropTarget; + + Component clickedComponent = null; + + public static void main(String[] args) throws Exception { + ModalDialogOnDragDeadlockTest test = new ModalDialogOnDragDeadlockTest(); + EventQueue.invokeAndWait(test::init); + try { + test.start(); + } finally { + EventQueue.invokeAndWait(() -> { + if (test.frame != null) { + test.frame.dispose(); + } + }); + } + } + + public void init() { + frame = new Frame("ModalDialogOnDragDeadlockTest"); + dialog = new Dialog(frame, "Modal dialog", true); + frame.setTitle("Test frame"); + frame.setBounds(100, 100, 200, 200); + + dragSource = DragSource.getDefaultDragSource(); + transferable = new StringSelection("TEXT"); + dragGestureListener = new DragGestureListener() { + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, transferable); + } + }; + dragGestureRecognizer = + dragSource.createDefaultDragGestureRecognizer(frame, DnDConstants.ACTION_COPY, + dragGestureListener); + dropTargetListener = new DropTargetAdapter() { + public void dragOver(DropTargetDragEvent dtde) { + dialog.setBounds(200, 200, 200, 200); + dialog.setVisible(true); + } + public void drop(DropTargetDropEvent dtde) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + dtde.dropComplete(true); + } + }; + dropTarget = new DropTarget(frame, dropTargetListener); + + frame.getToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.setVisible(true); + } + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void start() throws Exception { + finished = false; + Robot robot = new Robot(); + robot.waitForIdle(); + + Thread.sleep(FRAME_ACTIVATION_TIMEOUT); + EventQueue.invokeAndWait(() -> { + srcPoint = frame.getLocationOnScreen(); + d = frame.getSize(); + }); + srcPoint.translate(d.width / 2, d.height / 2); + + if (!pointInComponent(robot, srcPoint, frame)) { + System.err.println("WARNING: Couldn't locate source frame."); + return; + } + + final Point dstPoint = new Point(srcPoint); + dstPoint.translate(d.width / 4, d.height / 4); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + for (;!srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + Thread.sleep(50); + } + + robot.mouseRelease(InputEvent.BUTTON1_MASK); + + Thread.sleep(DROP_COMPLETION_TIMEOUT); + } + + public void reset() { + clickedComponent = null; + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component)e.getSource(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + } + + boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + robot.waitForIdle(); + reset(); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_MASK); + SYNC_LOCK.wait(MOUSE_RELEASE_TIMEOUT); + } + + Component c = clickedComponent; + + while (c != null && c != comp) { + c = c.getParent(); + } + + return c == comp; + } +} \ No newline at end of file diff --git a/test/jdk/java/awt/dnd/ModalDialogOnDropDeadlockTest.java b/test/jdk/java/awt/dnd/ModalDialogOnDropDeadlockTest.java new file mode 100644 index 00000000000..103cf9df12e --- /dev/null +++ b/test/jdk/java/awt/dnd/ModalDialogOnDropDeadlockTest.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Robot; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.DragSourceAdapter; +import java.awt.dnd.DragSourceDropEvent; +import java.awt.dnd.DragSourceListener; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetListener; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/* + @test + @bug 4623377 + @summary verifies that showing a modal dialog on drop doesn't hang + @key headful + @run main ModalDialogOnDropDeadlockTest +*/ + +public class ModalDialogOnDropDeadlockTest implements AWTEventListener { + + volatile Frame frame; + volatile Dialog dialog; + volatile Point srcPoint; + volatile Dimension d; + volatile boolean finished = false; + + static final Object SYNC_LOCK = new Object(); + static final int FRAME_ACTIVATION_TIMEOUT = 3000; + static final int DROP_COMPLETION_TIMEOUT = 5000; + static final int MOUSE_RELEASE_TIMEOUT = 1000; + + volatile MouseListener mouseListener; + + volatile Runnable dialogLocator; + volatile DragSourceListener dragSourceListener; + volatile DragSource dragSource; + volatile Transferable transferable; + volatile DragGestureListener dragGestureListener; + volatile DragGestureRecognizer dragGestureRecognizer; + volatile DropTargetListener dropTargetListener; + volatile DropTarget dropTarget; + + Component clickedComponent = null; + + public static void main(String[] args) throws Exception { + ModalDialogOnDropDeadlockTest test = new ModalDialogOnDropDeadlockTest(); + EventQueue.invokeAndWait(test::init); + try { + test.start(); + } finally { + EventQueue.invokeAndWait(() -> { + if (test.frame != null) { + test.frame.dispose(); + } + }); + } + } + + public void init() { + frame = new Frame(); + dialog = new Dialog(frame, "Modal dialog", true); + frame.setTitle("ModalDialogOnDropDeadlockTest"); + frame.setBounds(100, 100, 200, 200); + + mouseListener = new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + dialog.dispose(); + } + }; + dialogLocator = new Runnable() { + public void run() { + try { + final Robot robot = new Robot(); + + Thread.sleep(FRAME_ACTIVATION_TIMEOUT); + + final Point srcPoint = dialog.getLocationOnScreen(); + Dimension d = dialog.getSize(); + srcPoint.translate(d.width / 2, d.height / 2); + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + + } catch (Exception e) { + e.printStackTrace(); + dialog.dispose(); + } + } + }; + dragSourceListener = new DragSourceAdapter() { + public void dragDropEnd(DragSourceDropEvent dsde) { + finished = true; + } + }; + dragSource = DragSource.getDefaultDragSource(); + transferable = new StringSelection("TEXT"); + dragGestureListener = new DragGestureListener() { + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, transferable, dragSourceListener); + } + }; + dragGestureRecognizer = + dragSource.createDefaultDragGestureRecognizer(frame, DnDConstants.ACTION_COPY, + dragGestureListener); + dropTargetListener = new DropTargetAdapter() { + public void drop(DropTargetDropEvent dtde) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + dialog.addMouseListener(mouseListener); + dialog.setBounds(200, 200, 200, 200); + new Thread(dialogLocator).start(); + dialog.setVisible(true); + dtde.dropComplete(true); + } + }; + dropTarget = new DropTarget(frame, dropTargetListener); + + frame.getToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); + frame.setVisible(true); + } + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + public void start() throws Exception { + Robot robot = new Robot(); + robot.waitForIdle(); + + Thread.sleep(FRAME_ACTIVATION_TIMEOUT); + + EventQueue.invokeAndWait(() -> { + srcPoint = frame.getLocationOnScreen(); + d = frame.getSize(); + }); + srcPoint.translate(d.width / 2, d.height / 2); + + if (!pointInComponent(robot, srcPoint, frame)) { + System.err.println("WARNING: Couldn't locate source frame."); + return; + } + + final Point dstPoint = new Point(srcPoint); + dstPoint.translate(d.width / 4, d.height / 4); + + robot.mouseMove(srcPoint.x, srcPoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + for (;!srcPoint.equals(dstPoint); + srcPoint.translate(sign(dstPoint.x - srcPoint.x), + sign(dstPoint.y - srcPoint.y))) { + robot.mouseMove(srcPoint.x, srcPoint.y); + Thread.sleep(50); + } + + robot.mouseRelease(InputEvent.BUTTON1_MASK); + + Thread.sleep(DROP_COMPLETION_TIMEOUT); + + if (!finished) { + throw new RuntimeException("DnD not finished"); + } + } + + public void reset() { + clickedComponent = null; + } + + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_RELEASED) { + clickedComponent = (Component)e.getSource(); + synchronized (SYNC_LOCK) { + SYNC_LOCK.notifyAll(); + } + } + } + + boolean pointInComponent(Robot robot, Point p, Component comp) + throws InterruptedException { + robot.waitForIdle(); + reset(); + robot.mouseMove(p.x, p.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + synchronized (SYNC_LOCK) { + robot.mouseRelease(InputEvent.BUTTON1_MASK); + SYNC_LOCK.wait(MOUSE_RELEASE_TIMEOUT); + } + + Component c = clickedComponent; + + while (c != null && c != comp) { + c = c.getParent(); + } + + return c == comp; + } +}