1/*
2 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @key headful
27 * @bug 8024061
28 * @summary Checks that no exception is thrown if dragGestureRecognized
29 *          takes a while to complete.
30 * @library ../../../../lib/testlibrary
31 * @build jdk.testlibrary.OSInfo
32 * @run main bug8024061
33 */
34import java.awt.*;
35import java.awt.datatransfer.DataFlavor;
36import java.awt.datatransfer.Transferable;
37import java.awt.datatransfer.UnsupportedFlavorException;
38import java.awt.dnd.DnDConstants;
39import java.awt.dnd.DragGestureEvent;
40import java.awt.dnd.DragGestureListener;
41import java.awt.dnd.DragSource;
42import java.awt.dnd.DragSourceDragEvent;
43import java.awt.dnd.DragSourceDropEvent;
44import java.awt.dnd.DragSourceEvent;
45import java.awt.dnd.DragSourceListener;
46import java.awt.dnd.DropTarget;
47import java.awt.dnd.DropTargetDragEvent;
48import java.awt.dnd.DropTargetDropEvent;
49import java.awt.dnd.DropTargetEvent;
50import java.awt.dnd.DropTargetListener;
51import java.awt.event.InputEvent;
52
53import java.io.IOException;
54import java.lang.reflect.InvocationTargetException;
55import java.util.concurrent.CountDownLatch;
56import java.util.concurrent.TimeUnit;
57
58import javax.swing.*;
59import jdk.testlibrary.OSInfo;
60
61
62/**
63 * If dragGestureRecognized() takes a while to complete and if user performs a drag quickly,
64 * an exception is thrown from DropTargetListener.dragEnter when it calls
65 * DropTargetDragEvent.getTransferable().
66 * <p>
67 * This class introduces a delay in dragGestureRecognized() to cause the exception.
68 */
69public class bug8024061 {
70    private static final DataFlavor DropObjectFlavor;
71    private static final int DELAY = 1000;
72
73    static final DnDPanel panel1 = new DnDPanel(Color.yellow);
74    static final DnDPanel panel2 = new DnDPanel(Color.pink);
75    private final JFrame frame;
76    static Point here;
77    static Point there;
78    static Dimension d;
79
80
81
82    private static final CountDownLatch lock = new CountDownLatch(1);
83    private static volatile Exception dragEnterException = null;
84
85    static {
86        DataFlavor flavor = null;
87        try {
88            flavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType);
89        } catch (ClassNotFoundException e) {
90            e.printStackTrace();
91        }
92        DropObjectFlavor = flavor;
93    }
94
95    bug8024061() {
96        frame = new JFrame("DnDWithRobot");
97        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
98
99        d = new Dimension(100, 100);
100
101        panel1.setPreferredSize(d);
102        panel2.setPreferredSize(d);
103
104        Container content = frame.getContentPane();
105        content.setLayout(new GridLayout(1, 2, 5, 5));
106        content.add(panel1);
107        content.add(panel2);
108
109        frame.pack();
110
111        DropObject drop = new DropObject();
112        drop.place(panel1, new Point(10, 10));
113        frame.setVisible(true);
114    }
115
116    public static void main(String[] args) throws AWTException, InvocationTargetException, InterruptedException {
117        OSInfo.OSType type = OSInfo.getOSType();
118        if (type != OSInfo.OSType.LINUX && type != OSInfo.OSType.SOLARIS) {
119            System.out.println("This test is for Linux and Solaris only... " +
120                               "skipping!");
121            return;
122        }
123
124        final bug8024061[] dnd = {null};
125        SwingUtilities.invokeAndWait(new Runnable() {
126            @Override
127            public void run() {
128                dnd[0] = new bug8024061();
129            }
130        });
131        final Robot robot = new Robot();
132        robot.setAutoDelay(10);
133        robot.waitForIdle();
134        robot.delay(200);
135
136        JFrame frame = dnd[0].frame;
137        SwingUtilities.invokeAndWait(() -> {
138            here = panel1.getLocationOnScreen();
139            there = panel2.getLocationOnScreen();
140        });
141        here.translate(d.width / 2, d.height / 2);
142        there.translate(d.width / 2, d.height / 2);
143        robot.mouseMove(here.x, here.y);
144        robot.mousePress(InputEvent.BUTTON1_MASK);
145        while (here.x < there.x) {
146            here.x += 20;
147            robot.mouseMove(here.x, here.y);
148            System.out.println("x = " + here.x);
149        }
150        robot.mouseRelease(InputEvent.BUTTON1_MASK);
151        robot.waitForIdle();
152        robot.mousePress(InputEvent.BUTTON1_MASK);
153        robot.mouseRelease(InputEvent.BUTTON1_MASK);
154        System.out.println("finished");
155
156        try {
157            if (lock.await(5, TimeUnit.SECONDS)) {
158                if (dragEnterException == null) {
159                    System.out.println("Test passed.");
160                } else {
161                    System.out.println("Test failed.");
162                    dragEnterException.printStackTrace();
163                    throw new RuntimeException(dragEnterException);
164                }
165            } else {
166                System.out.println("Test failed. Timeout reached");
167                throw new RuntimeException("Timed out waiting for dragEnter()");
168            }
169        } finally {
170            SwingUtilities.invokeLater(frame::dispose);
171        }
172    }
173
174    class DropObject implements Transferable {
175        DnDPanel panel;
176        Color color = Color.CYAN;
177        int width = 50;
178        int height = 50;
179        int x;
180        int y;
181
182        void draw(Graphics2D g) {
183            Color savedColor = g.getColor();
184            g.setColor(color);
185            g.fillRect(x, y, width, height);
186            g.setColor(Color.lightGray);
187            g.drawRect(x, y, width, height);
188            g.setColor(savedColor);
189        }
190
191        boolean contains(int x, int y) {
192            return (x > this.x && x < this.x + width)
193                    && (y > this.y && y < this.y + height);
194        }
195
196        @Override
197        public DataFlavor[] getTransferDataFlavors() {
198            return new DataFlavor[]{DropObjectFlavor};
199        }
200
201        void place(DnDPanel panel, Point location) {
202            if (panel != this.panel) {
203                x = location.x;
204                y = location.y;
205                if (this.panel != null) {
206                    this.panel.setDropObject(null);
207                    this.panel.repaint();
208                }
209                this.panel = panel;
210                this.panel.setDropObject(this);
211                this.panel.repaint();
212            }
213        }
214
215        @Override
216        public boolean isDataFlavorSupported(DataFlavor flavor) {
217            return DropObjectFlavor.equals(flavor);
218        }
219
220        @Override
221        public Object getTransferData(DataFlavor flavor)
222                throws UnsupportedFlavorException, IOException {
223            if (isDataFlavorSupported(flavor)) {
224                return this;
225            } else {
226                throw new UnsupportedFlavorException(flavor);
227            }
228        }
229    }
230
231    static class DnDPanel extends JPanel {
232        DropObject dropObject;
233        final DragSource dragSource;
234        final DropTarget dropTarget;
235        final Color color;
236        final DragGestureListener dgListener;
237        final DragSourceListener dsListener;
238        final DropTargetListener dtListener;
239
240        DnDPanel(Color color) {
241            this.color = color;
242            this.dragSource = DragSource.getDefaultDragSource();
243            dgListener = new DragGestureListener() {
244                @Override
245                public void dragGestureRecognized(DragGestureEvent dge) {
246                    Point location = dge.getDragOrigin();
247                    if (dropObject != null && dropObject.contains(location.x, location.y)) {
248                        dragSource.startDrag(dge, DragSource.DefaultCopyNoDrop, dropObject, dsListener);
249                        try {
250                            Thread.sleep(DELAY);
251                        } catch (InterruptedException e) {
252                        }
253                    }
254                }
255            };
256
257            dsListener = new DragSourceListener() {
258                @Override
259                public void dragEnter(DragSourceDragEvent dsde) {
260                }
261
262                @Override
263                public void dragOver(DragSourceDragEvent dsde) {
264                }
265
266                @Override
267                public void dropActionChanged(DragSourceDragEvent dsde) {
268                }
269
270                @Override
271                public void dragExit(DragSourceEvent dse) {
272                }
273
274                @Override
275                public void dragDropEnd(DragSourceDropEvent dsde) {
276                }
277            };
278
279            dtListener = new DropTargetListener() {
280                @Override
281                public void dragEnter(DropTargetDragEvent dtde) {
282                    if (dropObject != null) {
283                        dtde.rejectDrag();
284                        return;
285                    }
286                    dtde.acceptDrag(DnDConstants.ACTION_MOVE);
287                    try {
288                        Transferable t = dtde.getTransferable();
289                        Object data = t.getTransferData(DropObjectFlavor);
290                    } catch (Exception e) {
291                        dragEnterException = e;
292                        e.printStackTrace();
293                    } finally {
294                        lock.countDown();
295                    }
296                }
297
298                @Override
299                public void dragOver(DropTargetDragEvent dtde) {
300                    if (dropObject != null) {
301                        dtde.rejectDrag();
302                        return;
303                    }
304                    dtde.acceptDrag(DnDConstants.ACTION_MOVE);
305                }
306
307                @Override
308                public void dropActionChanged(DropTargetDragEvent dtde) {
309                }
310
311                @Override
312                public void dragExit(DropTargetEvent dte) {
313                }
314
315                @Override
316                public void drop(DropTargetDropEvent dtde) {
317                    if (dropObject != null) {
318                        dtde.rejectDrop();
319                        return;
320                    }
321                    try {
322                        dtde.acceptDrop(DnDConstants.ACTION_MOVE);
323                        Transferable t = dtde.getTransferable();
324                        DropObject dropObject = (DropObject) t.getTransferData(DropObjectFlavor);
325                        Point location = dtde.getLocation();
326                        dropObject.place(DnDPanel.this, location);
327                        dtde.dropComplete(true);
328                    } catch (Exception e) {
329                        e.printStackTrace();
330                    }
331
332                }
333            };
334
335            dragSource.createDefaultDragGestureRecognizer(this,
336                    DnDConstants.ACTION_MOVE, dgListener);
337
338            dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, dtListener, true);
339
340        }
341
342        public void paintComponent(Graphics g) {
343            super.paintComponent(g);
344            Color savedColor = g.getColor();
345            g.setColor(color);
346            g.fillRect(0, 0, getWidth(), getHeight());
347            g.setColor(savedColor);
348            if (dropObject != null) {
349                dropObject.draw((Graphics2D) g);
350            }
351        }
352
353        void setDropObject(DropObject dropObject) {
354            this.dropObject = dropObject;
355        }
356
357        DropObject findDropObject(int x, int y) {
358            if (dropObject != null && dropObject.contains(x, y)) {
359                return dropObject;
360            }
361            return null;
362        }
363    }
364}
365