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  * @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     private final DnDPanel panel1 = new DnDPanel(Color.yellow);
  74     private final DnDPanel panel2 = new DnDPanel(Color.pink);
  75     private final JFrame frame;
  76 
  77     private static final CountDownLatch lock = new CountDownLatch(1);
  78     private static volatile Exception dragEnterException = null;
  79 
  80     static {
  81         DataFlavor flavor = null;
  82         try {
  83             flavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType);
  84         } catch (ClassNotFoundException e) {
  85             e.printStackTrace();
  86         }
  87         DropObjectFlavor = flavor;
  88     }
  89 
  90     bug8024061() {
  91         frame = new JFrame("DnDWithRobot");
  92         frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  93 
  94         Dimension d = new Dimension(100, 100);
  95 
  96         panel1.setPreferredSize(d);
  97         panel2.setPreferredSize(d);
  98 
  99         Container content = frame.getContentPane();
 100         content.setLayout(new GridLayout(1, 2, 5, 5));
 101         content.add(panel1);
 102         content.add(panel2);
 103 
 104         frame.pack();
 105 
 106         DropObject drop = new DropObject();
 107         drop.place(panel1, new Point(10, 10));
 108         frame.setVisible(true);
 109     }
 110 
 111     public static void main(String[] args) throws AWTException, InvocationTargetException, InterruptedException {
 112         OSInfo.OSType type = OSInfo.getOSType();
 113         if (type != OSInfo.OSType.LINUX && type != OSInfo.OSType.SOLARIS) {
 114             System.out.println("This test is for Linux and Solaris only... " +
 115                                "skipping!");
 116             return;
 117         }
 118 
 119         final bug8024061[] dnd = {null};
 120         SwingUtilities.invokeAndWait(new Runnable() {
 121             @Override
 122             public void run() {
 123                 dnd[0] = new bug8024061();
 124             }
 125         });
 126         final Robot robot = new Robot();
 127         robot.setAutoDelay(10);
 128         robot.waitForIdle();
 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         robot.waitForIdle();
 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 }