1/*
2 * Copyright (c) 2008, 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
24/*
25  @test
26  @key headful
27  @bug       6607170
28  @summary   Tests for focus-auto-transfer.
29  @author    Anton Tarasov: area=awt-focus
30  @library   ../../regtesthelpers
31  @build     Util
32  @run       main ContainerFocusAutoTransferTest
33*/
34
35import java.applet.Applet;
36import java.awt.AWTEvent;
37import java.awt.Component;
38import java.awt.ComponentOrientation;
39import java.awt.DefaultKeyboardFocusManager;
40import java.awt.KeyboardFocusManager;
41import java.awt.Robot;
42import java.awt.Color;
43import java.awt.FlowLayout;
44import java.awt.Toolkit;
45import java.awt.event.AWTEventListener;
46import java.awt.event.FocusEvent;
47import java.awt.event.WindowEvent;
48import javax.swing.JButton;
49import javax.swing.JFrame;
50import javax.swing.JPanel;
51import test.java.awt.regtesthelpers.Util;
52
53public class ContainerFocusAutoTransferTest extends Applet {
54    Robot robot;
55    TestFrame frame;
56    KeyboardFocusManager kfm;
57    enum TestCase {
58        REMOVAL { public String toString() { return "removal"; } },
59        HIDING { public String toString() { return "hiding"; } },
60        DISABLING { public String toString() { return "disabling"; } },
61        DEFOCUSING { public String toString() { return "defocusing"; } };
62        public abstract String toString();
63    };
64
65    public static void main(String[] args) {
66        ContainerFocusAutoTransferTest app = new ContainerFocusAutoTransferTest();
67        app.init();
68        app.start();
69    }
70
71    public void init() {
72        robot = Util.createRobot();
73        kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
74        Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
75            public void eventDispatched(AWTEvent event) {
76                System.out.println("--> " + event);
77            }
78        }, FocusEvent.FOCUS_EVENT_MASK | WindowEvent.WINDOW_FOCUS_EVENT_MASK);
79    }
80
81    public void start() {
82        System.out.println("*** TEST #1 ***");
83        test(TestCase.HIDING);
84
85        System.out.println("*** TEST #2 ***");
86        test(TestCase.REMOVAL);
87
88        System.out.println("*** TEST #3 ***");
89        test3(TestCase.DISABLING);
90
91        System.out.println("*** TEST #4 ***");
92        test3(TestCase.DEFOCUSING);
93
94        System.out.println("*** TEST #5 ***");
95        test4();
96
97        System.out.println("Test passed.");
98    }
99
100    void test(final TestCase t) {
101        showFrame();
102        test1(t); // Test for correct auto-transfer
103        test2(t); // Test for clearing focus
104    }
105
106    void test1(final TestCase t) {
107        Runnable action = new Runnable() {
108            public void run() {
109                KeyboardFocusManager.setCurrentKeyboardFocusManager(new TestKFM());
110                if (t == TestCase.REMOVAL) {
111                    frame.remove(frame.panel0);
112
113                } else if (t == TestCase.HIDING) {
114                    frame.panel0.setVisible(false);
115                }
116                frame.repaint();
117            }
118        };
119        if (!Util.trackFocusGained(frame.b3, action, 2000, false)) {
120            throw new TestFailedException(t + ": focus wasn't transfered as expected!");
121        }
122        KeyboardFocusManager.setCurrentKeyboardFocusManager(kfm);
123    }
124
125    void test2(TestCase t) {
126        frame.setFocusable(false); // exclude it from the focus cycle
127        if (t == TestCase.REMOVAL) {
128            frame.remove(frame.panel1);
129
130        } else if (t == TestCase.HIDING) {
131            frame.panel1.setVisible(false);
132        }
133        frame.repaint();
134        Util.waitForIdle(robot);
135        if (kfm.getFocusOwner() != null) {
136            throw new TestFailedException(t + ": focus wasn't cleared!");
137        }
138    }
139
140    void test3(final TestCase t) {
141        showFrame();
142        Runnable action = new Runnable() {
143            public void run() {
144                if (t == TestCase.DISABLING) {
145                    frame.b0.setEnabled(false);
146
147                } else if (t == TestCase.DEFOCUSING) {
148                    frame.b0.setFocusable(false);
149                }
150            }};
151        if (!Util.trackFocusGained(frame.b1, action, 2000, false)) {
152            throw new TestFailedException(t + ": focus wasn't transfered as expected!");
153        }
154    }
155
156    void test4() {
157        showFrame();
158        frame.setFocusableWindowState(false);
159        Util.waitForIdle(robot);
160        if (kfm.getFocusOwner() != null) {
161            throw new TestFailedException("defocusing the frame: focus wasn't cleared!");
162        }
163    }
164
165    void showFrame() {
166        if (frame != null) {
167            frame.dispose();
168            Util.waitForIdle(robot);
169        }
170        frame = new TestFrame();
171        frame.setVisible(true);
172        Util.waitTillShown(frame);
173
174        if (!frame.b0.hasFocus()) {
175            Util.clickOnComp(frame.b0, robot);
176            Util.waitForIdle(robot);
177            if (!frame.b0.hasFocus()) {
178                throw new TestErrorException("couldn't set focus on " + frame.b2);
179            }
180        }
181    }
182
183    class TestKFM extends DefaultKeyboardFocusManager {
184        public boolean dispatchEvent(AWTEvent e) {
185            if (e.getID() == FocusEvent.FOCUS_GAINED) {
186                System.out.println(e);
187                Component src = (Component)e.getSource();
188                if (src == frame.b1 || src == frame.b2) {
189                    throw new TestFailedException("wrong focus transfer on removal!");
190                }
191            }
192            return super.dispatchEvent(e);
193        }
194    }
195}
196
197class TestFrame extends JFrame {
198    public JPanel panel0 = new JPanel();
199    public JPanel panel1 = new JPanel();
200    public JButton b0 = new JButton("b0");
201    public JButton b1 = new JButton("b1");
202    public JButton b2 = new JButton("b2");
203    public JButton b3 = new JButton("b3");
204    public JButton b4 = new JButton("b4");
205
206    public TestFrame() {
207        super("TestFrame");
208
209        // The change of the orientation and the reverse order of
210        // adding the buttons to the panel is because in Container.removeNotify()
211        // the child components are removed in the reverse order.
212        // We want that the focus owner (b0) would be removed first and
213        // that the next traversable component would be b1.
214        panel0.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
215        panel0.add(b2);
216        panel0.add(b1);
217        panel0.add(b0);
218
219        panel1.add(b3);
220        panel1.add(b4);
221
222        setLayout(new FlowLayout());
223        add(panel0);
224        add(panel1);
225        pack();
226
227        panel0.setBackground(Color.red);
228        panel1.setBackground(Color.blue);
229    }
230}
231
232// Thrown when the behavior being verified is found wrong.
233class TestFailedException extends RuntimeException {
234    TestFailedException(String msg) {
235        super("Test failed: " + msg);
236    }
237}
238
239// Thrown when an error not related to the behavior being verified is encountered.
240class TestErrorException extends RuntimeException {
241    TestErrorException(String msg) {
242        super("Unexpected error: " + msg);
243    }
244}
245