1/*
2 * Copyright (c) 2010, 2016, 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
24import java.awt.*;
25import java.awt.event.WindowAdapter;
26import java.awt.event.WindowEvent;
27import java.awt.event.WindowFocusListener;
28import java.awt.geom.Area;
29import java.awt.geom.GeneralPath;
30import java.awt.geom.Rectangle2D;
31import java.util.HashMap;
32
33/*
34 * @test
35 * @key headful
36 * @bug 8013450
37 * @summary Check if the window events (Focus and Activation) are triggered correctly
38 *          when clicked on visible and clipped areas.
39 *
40 * Test Description: Check if PERPIXEL_TRANSPARENT Translucency type is supported
41 *      by the current platform. Proceed if it is supported. Apply different
42 *      types of shapes on a Window. Make it appear with a known background.
43 *      Check if mouse events which result in window-activated events are
44 *      triggered only within the window's shape and not outside. Repeat this
45 *      for Window, Dialog and Frame.
46 * Expected Result: If PERPIXEL_TRANSPARENT Translucency type is supported, window should
47 *      gain focus and should trigger activated events only when it is clicked on the
48 *      visible areas. Events should be delivered to the background window if clicked
49 *      on the clipped areas.
50 *
51 * @author mrkam
52 * @author Dmitriy Ermashov (dmitriy.ermashov@oracle.com)
53 * @library ../../../../lib/testlibrary
54 * @build Common ExtendedRobot
55 * @run main FocusAWTTest
56 */
57
58public class FocusAWTTest extends Common {
59
60    ExtendedRobot robot;
61    int dx;
62    int dy;
63    static final int x = 20;
64    static final int y = 400;
65
66    static volatile HashMap<String, Boolean> flags = new HashMap<String, Boolean>();
67    static {
68        flags.put("backgroundWindowActivated", false);
69        flags.put("backgroundWindowDeactivated", false);
70        flags.put("backgroundWindowGotFocus", false);
71        flags.put("backgroundWindowLostFocus", false);
72        flags.put("foregroundWindowGotFocus", false);
73        flags.put("foregroundWindowLostFocus", false);
74        flags.put("foregroundWindowActivated", false);
75        flags.put("foregroundWindowDeactivated", false);
76    }
77
78    public static void main(String[] ignored) throws Exception{
79        if (checkTranslucencyMode(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT))
80            for (Class<Window> windowClass: WINDOWS_TO_TEST) {
81                new FocusAWTTest(windowClass).doTest();
82            }
83    }
84
85    public FocusAWTTest(Class windowClass) throws Exception {
86        super(windowClass);
87        this.robot = new ExtendedRobot();
88        robot.waitForIdle();
89        EventQueue.invokeAndWait(() -> {
90            dx = background.getX() - x;
91            dy = background.getY() - y;
92        });
93        robot.waitForIdle();
94    }
95
96    @Override
97    public void initBackgroundFrame() {
98        background = new Frame();
99        background.setSize(300, 300);
100        background.setLocation(x, y);
101        background.setFocusable(true);
102        background.setFocusableWindowState(true);
103
104        background.addWindowFocusListener(new WindowFocusListener() {
105            public void windowGainedFocus(WindowEvent e) { flags.put("backgroundWindowGotFocus", true); }
106            public void windowLostFocus(WindowEvent e) { flags.put("backgroundWindowLostFocus", true); }
107        });
108
109        background.addWindowListener(new WindowAdapter() {
110            public void windowActivated(WindowEvent e) { flags.put("backgroundWindowActivated", true); }
111            public void windowDeactivated(WindowEvent e) { flags.put("backgroundWindowDeactivated", true); }
112        });
113        background.add(new TextArea());
114        background.setVisible(true);
115    }
116
117    @Override
118    public void initGUI() {
119        if (windowClass.equals(Frame.class)) {
120            window = new Frame() {
121                public void paint(Graphics g) {
122                    g.setColor(Color.BLUE);
123                    g.fillRect(0, 0, 200, 200);
124                }
125            };
126            ((Frame) window).setUndecorated(true);
127        } else if (windowClass.equals(Dialog.class)) {
128            window = new Dialog(background) {
129                public void paint(Graphics g) {
130                    g.setColor(Color.BLUE);
131                    g.fillRect(0, 0, 200, 200);
132                }
133            };
134            ((Dialog) window).setUndecorated(true);
135        } else {
136            window = new Window(background) {
137                public void paint(Graphics g) {
138                    g.setColor(Color.BLUE);
139                    g.fillRect(0, 0, 200, 200);
140                }
141            };
142            window.setFocusable(true);
143            window.setFocusableWindowState(true);
144        }
145
146        window.setPreferredSize(new Dimension(200, 200));
147        window.setLocation(70 + dx, 450 + dy);
148        window.setLayout(new BorderLayout());
149
150        window.addWindowFocusListener(new WindowFocusListener() {
151            public void windowGainedFocus(WindowEvent e) { flags.put("foregroundWindowGotFocus", true); }
152            public void windowLostFocus(WindowEvent e) { flags.put("foregroundWindowLostFocus", true); }
153        });
154
155        window.addWindowListener(new WindowAdapter() {
156            public void windowActivated(WindowEvent e) { flags.put("foregroundWindowActivated", true); }
157            public void windowDeactivated(WindowEvent e) { flags.put("foregroundWindowDeactivated", true); }
158        });
159
160        applyShape();
161        window.pack();
162        window.setAlwaysOnTop(true);
163        window.setVisible(true);
164    }
165
166    @Override
167    public void doTest() throws Exception {
168        super.doTest();
169        final Point wls = new Point();
170        final Dimension size = new Dimension();
171        EventQueue.invokeAndWait(() -> {
172            window.requestFocus();
173            wls.setLocation(window.getLocationOnScreen());
174            window.getSize(size);
175        });
176
177        robot.waitForIdle();
178
179        check(wls.x + size.width - 5, wls.y + 5, wls.x + size.width / 3, wls.y + size.height / 3);
180        check(wls.x + size.width / 2, wls.y + size.height / 2, wls.x + size.width * 2 / 3, wls.y + size.height * 2 / 3);
181
182        EventQueue.invokeAndWait(() -> {
183            background.dispose();
184            window.dispose();
185        });
186
187        robot.waitForIdle();
188    }
189
190    @Override
191    public void applyShape() {
192        Shape shape;
193        Area a = new Area(new Rectangle2D.Float(0, 0, 200, 200));
194        GeneralPath gp;
195        gp = new GeneralPath();
196        gp.moveTo(190, 0);
197        gp.lineTo(200, 0);
198        gp.lineTo(200, 10);
199        gp.lineTo(10, 200);
200        gp.lineTo(0, 200);
201        gp.lineTo(0, 190);
202        gp.closePath();
203        a.subtract(new Area(gp));
204        shape = a;
205
206        window.setShape(shape);
207    }
208
209    private void check(int xb, int yb, int xw, int yw) throws Exception {
210        checkClick(xb, yb, "backgroundWindowGotFocus");
211        checkClick(xw, yw, "foregroundWindowGotFocus");
212        checkClick(xb, yb, "foregroundWindowLostFocus");
213        checkClick(xw, yw, "backgroundWindowLostFocus");
214
215        if (window instanceof Dialog || window instanceof Frame) {
216            checkClick(xb, yb, "backgroundWindowActivated");
217            checkClick(xw, yw, "foregroundWindowActivated");
218            checkClick(xb, yb, "foregroundWindowDeactivated");
219            checkClick(xw, yw, "backgroundWindowDeactivated");
220        }
221
222    }
223
224    private void checkClick(int x, int y, String flag) throws Exception {
225        System.out.println("Trying to click point " + x + ", " + y + ", looking for " + flag + " to trigger.");
226
227        flags.put(flag, false);
228
229        robot.mouseMove(x, y);
230        robot.click();
231        int i = 0;
232        while (i < 5000 && !flags.get(flag)) {
233            robot.waitForIdle(50);
234            i += 50;
235        }
236
237        if (!flags.get(flag))
238            throw new RuntimeException(flag + " is not triggered for click on point " + x + ", " + y + " for " + windowClass + "!");
239    }
240}
241