1 /*
2  * Copyright (c) 2016, 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 /**
25  * @test
26  * @key headful
27  * @bug 8154043 8172509
28  * @summary Fields not reachable anymore by tab-key, because of new tabbing
29  * behaviour of radio button groups.
30  * @run main ButtonGroupLayoutTraversalTest
31  */
32 
33 import java.awt.BorderLayout;
34 import java.awt.Color;
35 import java.awt.GridLayout;
36 import java.awt.Robot;
37 import java.awt.event.FocusAdapter;
38 import java.awt.event.FocusEvent;
39 import java.awt.event.KeyEvent;
40 import javax.swing.JFrame;
41 import javax.swing.SwingUtilities;
42 import javax.swing.UIManager;
43 import javax.swing.JPanel;
44 import javax.swing.ButtonGroup;
45 import javax.swing.JComponent;
46 import javax.swing.JRadioButton;
47 import javax.swing.JToggleButton;
48 import javax.swing.LayoutFocusTraversalPolicy;
49 import javax.swing.UnsupportedLookAndFeelException;
50 
51 public class ButtonGroupLayoutTraversalTest {
52 
53     static int nx = 3;
54     static int ny = 3;
55 
56     static int focusCnt[] = new int[nx * ny];
57     private static JFrame window;
58 
main(String[] args)59     public static void main(String[] args) throws Exception {
60 
61         SwingUtilities.invokeAndWait(() -> changeLAF());
62         SwingUtilities.invokeAndWait(() -> initLayout(nx, ny));
63         Robot robot = new Robot();
64         robot.setAutoDelay(100);
65         robot.waitForIdle();
66         robot.delay(200);
67 
68         for (int i = 0; i < nx * ny - nx * ny / 2 - 1; i++) {
69             robot.keyPress(KeyEvent.VK_RIGHT);
70             robot.keyRelease(KeyEvent.VK_RIGHT);
71         }
72 
73         for (int i = 0; i < nx * ny / 2; i++) {
74             robot.keyPress(KeyEvent.VK_TAB);
75             robot.keyRelease(KeyEvent.VK_TAB);
76         }
77 
78         robot.waitForIdle();
79         robot.delay(200);
80 
81         for (int i = 0; i < nx * ny; i++) {
82             if (focusCnt[i] < 1) {
83                 SwingUtilities.invokeLater(window::dispose);
84                 throw new RuntimeException("Component " + i
85                         + " is not reachable in the forward focus cycle");
86             } else if (focusCnt[i] > 1) {
87                 SwingUtilities.invokeLater(window::dispose);
88                 throw new RuntimeException("Component " + i
89                         + " got focus more than once in the forward focus cycle");
90             }
91         }
92 
93         for (int i = 0; i < nx * ny / 2; i++) {
94             robot.keyPress(KeyEvent.VK_SHIFT);
95             robot.keyPress(KeyEvent.VK_TAB);
96             robot.keyRelease(KeyEvent.VK_TAB);
97             robot.keyRelease(KeyEvent.VK_SHIFT);
98         }
99 
100         for (int i = 0; i < nx * ny - nx * ny / 2 - 1; i++) {
101             robot.keyPress(KeyEvent.VK_LEFT);
102             robot.keyRelease(KeyEvent.VK_LEFT);
103         }
104 
105         robot.keyPress(KeyEvent.VK_SHIFT);
106         robot.keyPress(KeyEvent.VK_TAB);
107         robot.keyRelease(KeyEvent.VK_TAB);
108         robot.keyRelease(KeyEvent.VK_SHIFT);
109 
110         robot.waitForIdle();
111         robot.delay(200);
112 
113         for (int i = 0; i < nx * ny; i++) {
114             if (focusCnt[i] < 2) {
115                 SwingUtilities.invokeLater(window::dispose);
116                 throw new RuntimeException("Component " + i
117                         + " is not reachable in the backward focus cycle");
118             } else if (focusCnt[i] > 2) {
119                 SwingUtilities.invokeLater(window::dispose);
120                 throw new RuntimeException("Component " + i
121                         + " got focus more than once in the backward focus cycle");
122             }
123         }
124 
125         SwingUtilities.invokeLater(window::dispose);
126     }
127 
changeLAF()128     private static void changeLAF() {
129         String currentLAF = UIManager.getLookAndFeel().toString();
130         currentLAF = currentLAF.toLowerCase();
131         if (currentLAF.contains("aqua") || currentLAF.contains("nimbus")) {
132             try {
133                 UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
134             } catch (ClassNotFoundException
135                     | IllegalAccessException
136                     | InstantiationException
137                     | UnsupportedLookAndFeelException ex) {
138                 ex.printStackTrace();
139             }
140         }
141     }
142 
initLayout(int nx, int ny)143     public static void initLayout(int nx, int ny) {
144         window = new JFrame("Test");
145         window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
146         JPanel rootPanel = new JPanel();
147         rootPanel.setLayout(new BorderLayout());
148         JPanel formPanel = new JPanel(new GridLayout(nx, ny));
149         formPanel.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
150         formPanel.setFocusCycleRoot(true);
151         ButtonGroup radioButtonGroup = new ButtonGroup();
152         for (int i = 0; i < nx * ny; i++) {
153             JToggleButton comp;
154             if (i % 2 == 0) {
155                 comp = new JRadioButton("Grouped component");
156                 radioButtonGroup.add(comp);
157             } else {
158                 comp = new JRadioButton("Single component");
159             }
160             formPanel.add(comp);
161             int fi = i;
162             comp.setBackground(Color.red);
163             comp.addFocusListener(new FocusAdapter() {
164                 @Override
165                 public void focusGained(FocusEvent e) {
166                     focusCnt[fi]++;
167                     if (focusCnt[fi] == 1) {
168                         ((JComponent) e.getSource())
169                                 .setBackground(Color.yellow);
170                     } else if (focusCnt[fi] == 2) {
171                         ((JComponent) e.getSource())
172                                 .setBackground(Color.green);
173                     } else {
174                         ((JComponent) e.getSource())
175                                 .setBackground(Color.red);
176                     }
177                 }
178             });
179         }
180         rootPanel.add(formPanel, BorderLayout.CENTER);
181         window.add(rootPanel);
182         window.pack();
183         window.setVisible(true);
184     }
185 }
186