1 /*
2  * Copyright (c) 2015, 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 import java.awt.Dialog;
25 import java.awt.Frame;
26 import java.awt.Point;
27 import java.awt.Robot;
28 import java.awt.Window;
29 import java.awt.datatransfer.DataFlavor;
30 import java.awt.datatransfer.Transferable;
31 import java.awt.dnd.DnDConstants;
32 import java.awt.dnd.DragGestureEvent;
33 import java.awt.dnd.DragGestureListener;
34 import java.awt.dnd.DragSource;
35 import java.awt.dnd.DropTarget;
36 import java.awt.dnd.DropTargetDragEvent;
37 import java.awt.dnd.DropTargetDropEvent;
38 import java.awt.dnd.DropTargetEvent;
39 import java.awt.dnd.DropTargetListener;
40 import java.awt.event.InputEvent;
41 import java.awt.event.MouseAdapter;
42 import java.awt.event.MouseEvent;
43 
44 import java.io.BufferedReader;
45 import java.io.File;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.InputStreamReader;
49 import java.util.concurrent.TimeUnit;
50 
51 /*
52  * @test
53  * @key headful
54  * @bug 8134917 8139050
55  * @summary [macosx] JOptionPane doesn't receive mouse events when opened from a drop event
56  * @run main MissingEventsOnModalDialogTest RUN_PROCESS
57  */
58 public class MissingEventsOnModalDialogTest {
59 
60     private static final String RUN_PROCESS = "RUN_PROCESS";
61     private static final String RUN_TEST = "RUN_TEST";
62     private static boolean exception = false;
63     private static volatile boolean passed = false;
64 
main(String[] args)65     public static void main(String[] args) throws Exception {
66         String command = args.length < 1 ? RUN_TEST : args[0];
67         switch (command) {
68             case RUN_PROCESS:
69                 runProcess();
70                 break;
71             case RUN_TEST:
72                 runTest();
73                 break;
74             default:
75                 throw new RuntimeException("Unknown command: " + command);
76         }
77     }
78 
79     private static void runTest() throws Exception {
80         Frame sourceFrame = createFrame("Source Frame", 100, 100);
81         Frame targetFrame = createFrame("Target Frame", 350, 350);
82 
83         DragSource defaultDragSource
84                 = DragSource.getDefaultDragSource();
85         defaultDragSource.createDefaultDragGestureRecognizer(sourceFrame,
86                 DnDConstants.ACTION_COPY_OR_MOVE,
87                 new TestDragGestureListener());
88         new DropTarget(targetFrame, DnDConstants.ACTION_COPY_OR_MOVE,
89                 new TestDropTargetListener(targetFrame));
90 
91         Robot robot = new Robot();
92         robot.setAutoDelay(50);
93 
94         sourceFrame.toFront();
95         robot.waitForIdle();
96 
97         Point point = getCenterPoint(sourceFrame);
98         robot.mouseMove(point.x, point.y);
99         robot.waitForIdle();
100 
101         mouseDragAndDrop(robot, point, getCenterPoint(targetFrame));
102 
103         long time = System.currentTimeMillis() + 1000;
104 
105         while (!passed) {
106             if (time < System.currentTimeMillis()) {
107                 sourceFrame.dispose();
108                 targetFrame.dispose();
109                 throw new RuntimeException("Mouse clicked event is lost!");
110             }
111             Thread.sleep(10);
112         }
113         sourceFrame.dispose();
114         targetFrame.dispose();
115     }
116 
117     private static Frame createFrame(String title, int x, int y) {
118         Frame frame = new Frame();
119         frame.setSize(200, 200);
120         frame.setLocation(x, y);
121         frame.setTitle(title);
122         frame.setVisible(true);
123         return frame;
124     }
125 
126     private static Point getCenterPoint(Window window) {
127         Point centerPoint = window.getLocationOnScreen();
128         centerPoint.translate(window.getWidth() / 2, window.getHeight() / 2);
129         return centerPoint;
130     }
131 
132     public static void mouseDragAndDrop(Robot robot, Point from, Point to) {
133         mouseDND(robot, from.x, from.y, to.x, to.y);
134     }
135 
136     public static void mouseDND(Robot robot, int x1, int y1, int x2, int y2) {
137 
138         int N = 20;
139         int x = x1;
140         int y = y1;
141         int dx = (x2 - x1) / N;
142         int dy = (y2 - y1) / N;
143 
144         robot.mousePress(InputEvent.BUTTON1_MASK);
145 
146         for (int i = 0; i < N; i++) {
147             robot.mouseMove(x += dx, y += dy);
148         }
149 
150         robot.mouseRelease(InputEvent.BUTTON1_MASK);
151     }
152 
153     private static class TestDragGestureListener implements DragGestureListener {
154 
155         public void dragGestureRecognized(DragGestureEvent dge) {
156             dge.startDrag(null, new StringTransferable());
157         }
158     }
159 
160     static class StringTransferable implements Transferable {
161 
162         @Override
163         public DataFlavor[] getTransferDataFlavors() {
164             return new DataFlavor[]{DataFlavor.stringFlavor};
165         }
166 
167         @Override
168         public boolean isDataFlavorSupported(DataFlavor flavor) {
169             return flavor.equals(DataFlavor.stringFlavor);
170         }
171 
172         @Override
173         public Object getTransferData(DataFlavor flavor) {
174             return "Hello World!";
175         }
176     }
177 
178     private static class TestDropTargetListener implements DropTargetListener {
179 
180         private final Frame targetFrame;
181 
182         public TestDropTargetListener(Frame targetFrame) {
183             this.targetFrame = targetFrame;
184         }
185 
186         @Override
187         public void dragEnter(DropTargetDragEvent dtde) {
188             dtde.acceptDrag(dtde.getDropAction());
189         }
190 
191         @Override
192         public void dragOver(DropTargetDragEvent dtde) {
193             dtde.acceptDrag(dtde.getDropAction());
194         }
195 
196         @Override
197         public void dropActionChanged(DropTargetDragEvent dtde) {
198             dtde.acceptDrag(dtde.getDropAction());
199         }
200 
201         @Override
202         public void dragExit(DropTargetEvent dte) {
203         }
204 
205         @Override
206         public void drop(DropTargetDropEvent dtde) {
207             dtde.acceptDrop(dtde.getDropAction());
208             showModalDialog(targetFrame);
209             dtde.dropComplete(true);
210         }
211     }
212 
213     private static void showModalDialog(Frame targetFrame) {
214 
215         Dialog dialog = new Dialog(targetFrame, true);
216 
217         dialog.addMouseListener(new MouseAdapter() {
218 
219             @Override
220             public void mouseClicked(MouseEvent e) {
221                 passed = true;
222                 dialog.dispose();
223             }
224         });
225 
226         dialog.setSize(400, 300);
227         dialog.setTitle("Modal Dialog!");
228 
229         clickOnModalDialog(dialog);
230         dialog.setVisible(true);
231     }
232 
233     private static void clickOnModalDialog(Dialog dialog) {
234         new Thread(() -> {
235             clickOnDialog(dialog);
236         }).start();
237     }
238 
239     private static void clickOnDialog(Dialog dialog) {
240         try {
241             long time = System.currentTimeMillis() + 200;
242 
243             while (!dialog.isVisible()) {
244                 if (time < System.currentTimeMillis()) {
245                     throw new RuntimeException("Dialog is not visible!");
246                 }
247                 Thread.sleep(10);
248             }
249             Robot robot = new Robot();
250             robot.setAutoDelay(50);
251             robot.waitForIdle();
252             robot.delay(200);
253 
254             Point point = getCenterPoint(dialog);
255 
256             robot.mouseMove(point.x, point.y);
257             robot.mousePress(InputEvent.BUTTON1_MASK);
258             robot.mouseRelease(InputEvent.BUTTON1_MASK);
259 
260         } catch (Exception e) {
261             throw new RuntimeException(e);
262         }
263     }
264 
265     private static void runProcess() throws Exception {
266         String javaPath = System.getProperty("java.home", "");
267         String command = javaPath + File.separator + "bin" + File.separator + "java"
268                 + " " + MissingEventsOnModalDialogTest.class.getName() + " " + RUN_TEST;
269 
270         Process process = Runtime.getRuntime().exec(command);
271         boolean processExit = process.waitFor(20, TimeUnit.SECONDS);
272 
273         StringBuilder inStream = new StringBuilder();
274         StringBuilder errStream = new StringBuilder();
275         checkErrors(process.getErrorStream(), errStream);
276         checkErrors(process.getInputStream(), inStream);
277 
278         if (exception) {
279             System.out.println(inStream);
280             System.err.println(errStream);
281             throw new RuntimeException("Exception in the output!");
282         }
283 
284         if (!processExit) {
285             process.destroy();
286             throw new RuntimeException(""
287                     + "The sub process has not exited!");
288         }
289     }
290 
291     private static boolean containsError(String line) {
292         line = line.toLowerCase();
293         return line.contains("exception") || line.contains("error")
294                 || line.contains("selector");
295     }
296 
297     private static void checkErrors(InputStream in, StringBuilder stream) throws IOException {
298         try (BufferedReader bufferedReader
299                 = new BufferedReader(new InputStreamReader(in))) {
300 
301             String line = null;
302             while ((line = bufferedReader.readLine()) != null) {
303                 if (!exception) {
304                     exception = containsError(line);
305                 }
306                 stream.append(line).append("\n");
307             }
308         }
309     }
310 }
311