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 */ 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 import 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 */ 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 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 }