1 /* 2 * Copyright (c) 2014, 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 */ 31 import sun.awt.OSInfo; 32 import sun.awt.OSInfo.OSType; 33 import sun.awt.SunToolkit; 34 35 import java.awt.*; 36 import java.awt.datatransfer.DataFlavor; 37 import java.awt.datatransfer.Transferable; 38 import java.awt.datatransfer.UnsupportedFlavorException; 39 import java.awt.dnd.DnDConstants; 40 import java.awt.dnd.DragGestureEvent; 41 import java.awt.dnd.DragGestureListener; 42 import java.awt.dnd.DragSource; 43 import java.awt.dnd.DragSourceDragEvent; 44 import java.awt.dnd.DragSourceDropEvent; 45 import java.awt.dnd.DragSourceEvent; 46 import java.awt.dnd.DragSourceListener; 47 import java.awt.dnd.DropTarget; 48 import java.awt.dnd.DropTargetDragEvent; 49 import java.awt.dnd.DropTargetDropEvent; 50 import java.awt.dnd.DropTargetEvent; 51 import java.awt.dnd.DropTargetListener; 52 import java.awt.event.InputEvent; 53 54 import java.io.IOException; 55 import java.lang.reflect.InvocationTargetException; 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.TimeUnit; 58 59 import javax.swing.*; 60 61 /** 62 * If dragGestureRecognized() takes a while to complete and if user performs a drag quickly, 63 * an exception is thrown from DropTargetListener.dragEnter when it calls 64 * DropTargetDragEvent.getTransferable(). 65 * <p> 66 * This class introduces a delay in dragGestureRecognized() to cause the exception. 67 */ 68 public class bug8024061 { 69 private static final DataFlavor DropObjectFlavor; 70 private static final int DELAY = 1000; 71 72 private final DnDPanel panel1 = new DnDPanel(Color.yellow); 73 private final DnDPanel panel2 = new DnDPanel(Color.pink); 74 private final JFrame frame; 75 76 private static final CountDownLatch lock = new CountDownLatch(1); 77 private static volatile Exception dragEnterException = null; 78 79 static { 80 DataFlavor flavor = null; 81 try { 82 flavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); 83 } catch (ClassNotFoundException e) { 84 e.printStackTrace(); 85 } 86 DropObjectFlavor = flavor; 87 } 88 89 bug8024061() { 90 frame = new JFrame("DnDWithRobot"); 91 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 92 93 Dimension d = new Dimension(100, 100); 94 95 panel1.setPreferredSize(d); 96 panel2.setPreferredSize(d); 97 98 Container content = frame.getContentPane(); 99 content.setLayout(new GridLayout(1, 2, 5, 5)); 100 content.add(panel1); 101 content.add(panel2); 102 103 frame.pack(); 104 105 DropObject drop = new DropObject(); 106 drop.place(panel1, new Point(10, 10)); 107 frame.setVisible(true); 108 } 109 110 public static void main(String[] args) throws AWTException, InvocationTargetException, InterruptedException { 111 OSType type = OSInfo.getOSType(); 112 if (type != OSType.LINUX && type != OSType.SOLARIS) { 113 System.out.println("This test is for Linux and Solaris only... " + 114 "skipping!"); 115 return; 116 } 117 118 final bug8024061[] dnd = {null}; 119 SwingUtilities.invokeAndWait(new Runnable() { 120 @Override 121 public void run() { 122 dnd[0] = new bug8024061(); 123 } 124 }); 125 final Robot robot = new Robot(); 126 robot.setAutoDelay(10); 127 SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); 128 toolkit.realSync(); 129 130 JFrame frame = dnd[0].frame; 131 Point point = frame.getLocationOnScreen(); 132 Point here = new Point(point.x + 35, point.y + 45); 133 Point there = new Point(point.x + 120, point.y + 45); 134 here.x += 25; 135 robot.mouseMove(here.x, here.y); 136 robot.mousePress(InputEvent.BUTTON1_MASK); 137 while (here.x < there.x) { 138 here.x += 20; 139 robot.mouseMove(here.x, here.y); 140 System.out.println("x = " + here.x); 141 } 142 robot.mouseRelease(InputEvent.BUTTON1_MASK); 143 toolkit.realSync(); 144 robot.mousePress(InputEvent.BUTTON1_MASK); 145 robot.mouseRelease(InputEvent.BUTTON1_MASK); 146 System.out.println("finished"); 147 148 try { 149 if (lock.await(5, TimeUnit.SECONDS)) { 150 if (dragEnterException == null) { 151 System.out.println("Test passed."); 152 } else { 153 System.out.println("Test failed."); 154 dragEnterException.printStackTrace(); 155 throw new RuntimeException(dragEnterException); 156 } 157 } else { 158 System.out.println("Test failed. Timeout reached"); 159 throw new RuntimeException("Timed out waiting for dragEnter()"); 160 } 161 } finally { 162 frame.dispose(); 163 } 164 } 165 166 class DropObject implements Transferable { 167 DnDPanel panel; 168 Color color = Color.CYAN; 169 int width = 50; 170 int height = 50; 171 int x; 172 int y; 173 174 void draw(Graphics2D g) { 175 Color savedColor = g.getColor(); 176 g.setColor(color); 177 g.fillRect(x, y, width, height); 178 g.setColor(Color.lightGray); 179 g.drawRect(x, y, width, height); 180 g.setColor(savedColor); 181 } 182 183 boolean contains(int x, int y) { 184 return (x > this.x && x < this.x + width) 185 && (y > this.y && y < this.y + height); 186 } 187 188 @Override 189 public DataFlavor[] getTransferDataFlavors() { 190 return new DataFlavor[]{DropObjectFlavor}; 191 } 192 193 void place(DnDPanel panel, Point location) { 194 if (panel != this.panel) { 195 x = location.x; 196 y = location.y; 197 if (this.panel != null) { 198 this.panel.setDropObject(null); 199 this.panel.repaint(); 200 } 201 this.panel = panel; 202 this.panel.setDropObject(this); 203 this.panel.repaint(); 204 } 205 } 206 207 @Override 208 public boolean isDataFlavorSupported(DataFlavor flavor) { 209 return DropObjectFlavor.equals(flavor); 210 } 211 212 @Override 213 public Object getTransferData(DataFlavor flavor) 214 throws UnsupportedFlavorException, IOException { 215 if (isDataFlavorSupported(flavor)) { 216 return this; 217 } else { 218 throw new UnsupportedFlavorException(flavor); 219 } 220 } 221 } 222 223 class DnDPanel extends JPanel { 224 DropObject dropObject; 225 final DragSource dragSource; 226 final DropTarget dropTarget; 227 final Color color; 228 final DragGestureListener dgListener; 229 final DragSourceListener dsListener; 230 final DropTargetListener dtListener; 231 232 DnDPanel(Color color) { 233 this.color = color; 234 this.dragSource = DragSource.getDefaultDragSource(); 235 dgListener = new DragGestureListener() { 236 @Override 237 public void dragGestureRecognized(DragGestureEvent dge) { 238 Point location = dge.getDragOrigin(); 239 if (dropObject != null && dropObject.contains(location.x, location.y)) { 240 dragSource.startDrag(dge, DragSource.DefaultCopyNoDrop, dropObject, dsListener); 241 try { 242 Thread.sleep(DELAY); 243 } catch (InterruptedException e) { 244 } 245 } 246 } 247 }; 248 249 dsListener = new DragSourceListener() { 250 @Override 251 public void dragEnter(DragSourceDragEvent dsde) { 252 } 253 254 @Override 255 public void dragOver(DragSourceDragEvent dsde) { 256 } 257 258 @Override 259 public void dropActionChanged(DragSourceDragEvent dsde) { 260 } 261 262 @Override 263 public void dragExit(DragSourceEvent dse) { 264 } 265 266 @Override 267 public void dragDropEnd(DragSourceDropEvent dsde) { 268 } 269 }; 270 271 dtListener = new DropTargetListener() { 272 @Override 273 public void dragEnter(DropTargetDragEvent dtde) { 274 if (dropObject != null) { 275 dtde.rejectDrag(); 276 return; 277 } 278 dtde.acceptDrag(DnDConstants.ACTION_MOVE); 279 try { 280 Transferable t = dtde.getTransferable(); 281 Object data = t.getTransferData(DropObjectFlavor); 282 } catch (Exception e) { 283 dragEnterException = e; 284 e.printStackTrace(); 285 } finally { 286 lock.countDown(); 287 } 288 } 289 290 @Override 291 public void dragOver(DropTargetDragEvent dtde) { 292 if (dropObject != null) { 293 dtde.rejectDrag(); 294 return; 295 } 296 dtde.acceptDrag(DnDConstants.ACTION_MOVE); 297 } 298 299 @Override 300 public void dropActionChanged(DropTargetDragEvent dtde) { 301 } 302 303 @Override 304 public void dragExit(DropTargetEvent dte) { 305 } 306 307 @Override 308 public void drop(DropTargetDropEvent dtde) { 309 if (dropObject != null) { 310 dtde.rejectDrop(); 311 return; 312 } 313 try { 314 dtde.acceptDrop(DnDConstants.ACTION_MOVE); 315 Transferable t = dtde.getTransferable(); 316 DropObject dropObject = (DropObject) t.getTransferData(DropObjectFlavor); 317 Point location = dtde.getLocation(); 318 dropObject.place(DnDPanel.this, location); 319 dtde.dropComplete(true); 320 } catch (Exception e) { 321 e.printStackTrace(); 322 } 323 324 } 325 }; 326 327 dragSource.createDefaultDragGestureRecognizer(this, 328 DnDConstants.ACTION_MOVE, dgListener); 329 330 dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, dtListener, true); 331 332 } 333 334 public void paintComponent(Graphics g) { 335 super.paintComponent(g); 336 Color savedColor = g.getColor(); 337 g.setColor(color); 338 g.fillRect(0, 0, getWidth(), getHeight()); 339 g.setColor(savedColor); 340 if (dropObject != null) { 341 dropObject.draw((Graphics2D) g); 342 } 343 } 344 345 void setDropObject(DropObject dropObject) { 346 this.dropObject = dropObject; 347 } 348 349 DropObject findDropObject(int x, int y) { 350 if (dropObject != null && dropObject.contains(x, y)) { 351 return dropObject; 352 } 353 return null; 354 } 355 } 356 }