1 /* 2 * Copyright (c) 2014, 2018, 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 /test/lib 31 * @build jdk.test.lib.Platform 32 * @run main bug8024061 33 */ 34 import java.awt.*; 35 import java.awt.datatransfer.DataFlavor; 36 import java.awt.datatransfer.Transferable; 37 import java.awt.datatransfer.UnsupportedFlavorException; 38 import java.awt.dnd.DnDConstants; 39 import java.awt.dnd.DragGestureEvent; 40 import java.awt.dnd.DragGestureListener; 41 import java.awt.dnd.DragSource; 42 import java.awt.dnd.DragSourceDragEvent; 43 import java.awt.dnd.DragSourceDropEvent; 44 import java.awt.dnd.DragSourceEvent; 45 import java.awt.dnd.DragSourceListener; 46 import java.awt.dnd.DropTarget; 47 import java.awt.dnd.DropTargetDragEvent; 48 import java.awt.dnd.DropTargetDropEvent; 49 import java.awt.dnd.DropTargetEvent; 50 import java.awt.dnd.DropTargetListener; 51 import java.awt.event.InputEvent; 52 53 import java.io.IOException; 54 import java.lang.reflect.InvocationTargetException; 55 import java.util.concurrent.CountDownLatch; 56 import java.util.concurrent.TimeUnit; 57 58 import javax.swing.*; 59 60 import jdk.test.lib.Platform; 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 */ 69 public 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 if (!Platform.isLinux()) { 118 System.out.println("This test is for Linux only... skipping!"); 119 return; 120 } 121 122 final bug8024061[] dnd = {null}; 123 SwingUtilities.invokeAndWait(new Runnable() { 124 @Override 125 public void run() { 126 dnd[0] = new bug8024061(); 127 } 128 }); 129 final Robot robot = new Robot(); 130 robot.setAutoDelay(10); 131 robot.waitForIdle(); 132 robot.delay(200); 133 134 JFrame frame = dnd[0].frame; 135 SwingUtilities.invokeAndWait(() -> { 136 here = panel1.getLocationOnScreen(); 137 there = panel2.getLocationOnScreen(); 138 }); 139 here.translate(d.width / 2, d.height / 2); 140 there.translate(d.width / 2, d.height / 2); 141 robot.mouseMove(here.x, here.y); 142 robot.mousePress(InputEvent.BUTTON1_MASK); 143 while (here.x < there.x) { 144 here.x += 20; 145 robot.mouseMove(here.x, here.y); 146 System.out.println("x = " + here.x); 147 } 148 robot.mouseRelease(InputEvent.BUTTON1_MASK); 149 robot.waitForIdle(); 150 robot.mousePress(InputEvent.BUTTON1_MASK); 151 robot.mouseRelease(InputEvent.BUTTON1_MASK); 152 System.out.println("finished"); 153 154 try { 155 if (lock.await(5, TimeUnit.SECONDS)) { 156 if (dragEnterException == null) { 157 System.out.println("Test passed."); 158 } else { 159 System.out.println("Test failed."); 160 dragEnterException.printStackTrace(); 161 throw new RuntimeException(dragEnterException); 162 } 163 } else { 164 System.out.println("Test failed. Timeout reached"); 165 throw new RuntimeException("Timed out waiting for dragEnter()"); 166 } 167 } finally { 168 SwingUtilities.invokeLater(frame::dispose); 169 } 170 } 171 172 class DropObject implements Transferable { 173 DnDPanel panel; 174 Color color = Color.CYAN; 175 int width = 50; 176 int height = 50; 177 int x; 178 int y; 179 180 void draw(Graphics2D g) { 181 Color savedColor = g.getColor(); 182 g.setColor(color); 183 g.fillRect(x, y, width, height); 184 g.setColor(Color.lightGray); 185 g.drawRect(x, y, width, height); 186 g.setColor(savedColor); 187 } 188 189 boolean contains(int x, int y) { 190 return (x > this.x && x < this.x + width) 191 && (y > this.y && y < this.y + height); 192 } 193 194 @Override 195 public DataFlavor[] getTransferDataFlavors() { 196 return new DataFlavor[]{DropObjectFlavor}; 197 } 198 199 void place(DnDPanel panel, Point location) { 200 if (panel != this.panel) { 201 x = location.x; 202 y = location.y; 203 if (this.panel != null) { 204 this.panel.setDropObject(null); 205 this.panel.repaint(); 206 } 207 this.panel = panel; 208 this.panel.setDropObject(this); 209 this.panel.repaint(); 210 } 211 } 212 213 @Override 214 public boolean isDataFlavorSupported(DataFlavor flavor) { 215 return DropObjectFlavor.equals(flavor); 216 } 217 218 @Override 219 public Object getTransferData(DataFlavor flavor) 220 throws UnsupportedFlavorException, IOException { 221 if (isDataFlavorSupported(flavor)) { 222 return this; 223 } else { 224 throw new UnsupportedFlavorException(flavor); 225 } 226 } 227 } 228 229 static class DnDPanel extends JPanel { 230 DropObject dropObject; 231 final DragSource dragSource; 232 final DropTarget dropTarget; 233 final Color color; 234 final DragGestureListener dgListener; 235 final DragSourceListener dsListener; 236 final DropTargetListener dtListener; 237 238 DnDPanel(Color color) { 239 this.color = color; 240 this.dragSource = DragSource.getDefaultDragSource(); 241 dgListener = new DragGestureListener() { 242 @Override 243 public void dragGestureRecognized(DragGestureEvent dge) { 244 Point location = dge.getDragOrigin(); 245 if (dropObject != null && dropObject.contains(location.x, location.y)) { 246 dragSource.startDrag(dge, DragSource.DefaultCopyNoDrop, dropObject, dsListener); 247 try { 248 Thread.sleep(DELAY); 249 } catch (InterruptedException e) { 250 } 251 } 252 } 253 }; 254 255 dsListener = new DragSourceListener() { 256 @Override 257 public void dragEnter(DragSourceDragEvent dsde) { 258 } 259 260 @Override 261 public void dragOver(DragSourceDragEvent dsde) { 262 } 263 264 @Override 265 public void dropActionChanged(DragSourceDragEvent dsde) { 266 } 267 268 @Override 269 public void dragExit(DragSourceEvent dse) { 270 } 271 272 @Override 273 public void dragDropEnd(DragSourceDropEvent dsde) { 274 } 275 }; 276 277 dtListener = new DropTargetListener() { 278 @Override 279 public void dragEnter(DropTargetDragEvent dtde) { 280 if (dropObject != null) { 281 dtde.rejectDrag(); 282 return; 283 } 284 dtde.acceptDrag(DnDConstants.ACTION_MOVE); 285 try { 286 Transferable t = dtde.getTransferable(); 287 Object data = t.getTransferData(DropObjectFlavor); 288 } catch (Exception e) { 289 dragEnterException = e; 290 e.printStackTrace(); 291 } finally { 292 lock.countDown(); 293 } 294 } 295 296 @Override 297 public void dragOver(DropTargetDragEvent dtde) { 298 if (dropObject != null) { 299 dtde.rejectDrag(); 300 return; 301 } 302 dtde.acceptDrag(DnDConstants.ACTION_MOVE); 303 } 304 305 @Override 306 public void dropActionChanged(DropTargetDragEvent dtde) { 307 } 308 309 @Override 310 public void dragExit(DropTargetEvent dte) { 311 } 312 313 @Override 314 public void drop(DropTargetDropEvent dtde) { 315 if (dropObject != null) { 316 dtde.rejectDrop(); 317 return; 318 } 319 try { 320 dtde.acceptDrop(DnDConstants.ACTION_MOVE); 321 Transferable t = dtde.getTransferable(); 322 DropObject dropObject = (DropObject) t.getTransferData(DropObjectFlavor); 323 Point location = dtde.getLocation(); 324 dropObject.place(DnDPanel.this, location); 325 dtde.dropComplete(true); 326 } catch (Exception e) { 327 e.printStackTrace(); 328 } 329 330 } 331 }; 332 333 dragSource.createDefaultDragGestureRecognizer(this, 334 DnDConstants.ACTION_MOVE, dgListener); 335 336 dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, dtListener, true); 337 338 } 339 340 public void paintComponent(Graphics g) { 341 super.paintComponent(g); 342 Color savedColor = g.getColor(); 343 g.setColor(color); 344 g.fillRect(0, 0, getWidth(), getHeight()); 345 g.setColor(savedColor); 346 if (dropObject != null) { 347 dropObject.draw((Graphics2D) g); 348 } 349 } 350 351 void setDropObject(DropObject dropObject) { 352 this.dropObject = dropObject; 353 } 354 355 DropObject findDropObject(int x, int y) { 356 if (dropObject != null && dropObject.contains(x, y)) { 357 return dropObject; 358 } 359 return null; 360 } 361 } 362 }