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