1 /*
2  * Copyright (c) 2013, 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    @key headful
26    @bug 4927934
27    @summary JTree traversal is unlike Native windows tree traversal
28    @author Andrey Pikalev
29    @run main bug4927934
30 */
31 
32 import java.awt.Robot;
33 import java.awt.event.FocusEvent;
34 import java.awt.event.FocusListener;
35 import java.awt.event.KeyEvent;
36 
37 import javax.swing.JFrame;
38 import javax.swing.JTree;
39 import javax.swing.SwingUtilities;
40 import javax.swing.UIManager;
41 import javax.swing.JScrollPane;
42 import javax.swing.event.TreeSelectionListener;
43 import javax.swing.event.TreeExpansionListener;
44 import javax.swing.event.TreeSelectionEvent;
45 import javax.swing.event.TreeExpansionEvent;
46 import javax.swing.tree.DefaultMutableTreeNode;
47 
48 public class bug4927934 implements TreeSelectionListener, TreeExpansionListener, FocusListener {
49 
50     final static Object listener = new bug4927934();
51 
52     static boolean focusGained = false;
53     public static boolean selectionChanged = false;
54     public static boolean treeExpanded = false;
55     public static boolean treeCollapsed = false;
56 
57     static JFrame frame;
58     static JTree tree;
59     static Robot robot;
60 
main(String args[])61     public static void main(String args[]) throws Exception {
62         UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
63 
64         robot = new Robot();
65         robot.setAutoDelay(50);
66 
67         try {
68             SwingUtilities.invokeAndWait(new Runnable() {
69                 public void run() {
70                     frame = new JFrame();
71 
72                     DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
73                     createNodes(root);
74                     tree = new JTree(root);
75                     JScrollPane scrollPane = new JScrollPane(tree);
76                     frame.getContentPane().add(scrollPane);
77 
78                     tree.addFocusListener((FocusListener) listener);
79                     tree.addTreeSelectionListener((TreeSelectionListener) listener);
80                     tree.addTreeExpansionListener((TreeExpansionListener) listener);
81 
82                     frame.setSize(300, 300);
83                     frame.setVisible(true);
84                 }
85             });
86 
87             robot.waitForIdle();
88             Thread.sleep(1000);
89 
90             SwingUtilities.invokeLater(new Runnable() {
91                 public void run() {
92                     tree.requestFocus();
93                 }
94             });
95 
96             synchronized (listener) {
97                 if (!focusGained) {
98                     System.out.println("waiting focusGained...");
99                     try {
100                         listener.wait(10000);
101                     } catch (InterruptedException e) {
102                         e.printStackTrace();
103                     }
104                 }
105             }
106 
107             // GO TO RIGHT
108             selectionChanged = false;
109             hitKey(KeyEvent.VK_RIGHT);
110             robot.waitForIdle();
111             if (!checkSelectionChanged(tree, 0)) {
112                 throw new RuntimeException("Root should be selected");
113             }
114 
115             selectionChanged = false;
116             hitKey(KeyEvent.VK_RIGHT);
117             robot.waitForIdle();
118             if (!checkSelectionChanged(tree, 1)) {
119                 throw new RuntimeException("Node should be selected");
120             }
121 
122             treeExpanded = false;
123             hitKey(KeyEvent.VK_RIGHT);
124             robot.waitForIdle();
125             if (!isTreeExpanded()) {
126                 throw new RuntimeException("Node should be expanded");
127             }
128 
129             selectionChanged = false;
130             hitKey(KeyEvent.VK_RIGHT);
131             robot.waitForIdle();
132             if (!checkSelectionChanged(tree, 2)) {
133                 throw new RuntimeException("Leaf1 should be selected");
134             }
135 
136             selectionChanged = false;
137             hitKey(KeyEvent.VK_RIGHT);
138             robot.waitForIdle();
139             if (!checkSelectionChanged(tree, 2)) {
140                 throw new RuntimeException("Leaf1 should be selected");
141             }
142 
143             // GO TO LEFT
144             selectionChanged = false;
145             hitKey(KeyEvent.VK_LEFT);
146             robot.waitForIdle();
147             if (!checkSelectionChanged(tree, 1)) {
148                 throw new RuntimeException("Node should be selected");
149             }
150 
151             treeCollapsed = false;
152             hitKey(KeyEvent.VK_LEFT);
153             if (!isTreeCollapsed()) {
154                 throw new RuntimeException("Node should be collapsed");
155             }
156 
157             selectionChanged = false;
158             hitKey(KeyEvent.VK_LEFT);
159             robot.waitForIdle();
160             if (!checkSelectionChanged(tree, 0)) {
161                 throw new RuntimeException("Root should be selected");
162             }
163 
164             treeCollapsed = false;
165             hitKey(KeyEvent.VK_LEFT);
166             robot.waitForIdle();
167             if (!isTreeCollapsed()) {
168                 throw new RuntimeException("Root should be collapsed");
169             }
170         } finally {
171             if (frame != null) {
172                 SwingUtilities.invokeAndWait(frame::dispose);
173             }
174         }
175     }
176 
177 
focusLost(FocusEvent e)178     synchronized public void focusLost(FocusEvent e) {
179     }
180 
focusGained(FocusEvent e)181     synchronized public void focusGained(FocusEvent e) {
182         focusGained = true;
183         System.out.println("focusGained");
184         listener.notifyAll();
185     }
186 
createNodes(DefaultMutableTreeNode root)187     private static void createNodes(DefaultMutableTreeNode root) {
188         DefaultMutableTreeNode node = new DefaultMutableTreeNode("Node");
189         node.add(new DefaultMutableTreeNode("Leaf1"));
190         node.add(new DefaultMutableTreeNode("Leaf2"));
191         root.add(node);
192         root.add(new DefaultMutableTreeNode("Leaf3"));
193     }
194 
valueChanged(TreeSelectionEvent e)195     synchronized public void valueChanged(TreeSelectionEvent e) {
196         selectionChanged = true;
197         System.out.println("selectionChanged");
198         notifyAll();
199     }
200 
treeCollapsed(TreeExpansionEvent e)201     synchronized public void treeCollapsed(TreeExpansionEvent e) {
202         System.out.println("treeCollapsed");
203         treeCollapsed = true;
204         notifyAll();
205     }
206 
treeExpanded(TreeExpansionEvent e)207     synchronized public void treeExpanded(TreeExpansionEvent e) {
208         System.out.println("treeExpanded");
209         treeExpanded = true;
210         notifyAll();
211     }
212 
hitKey(int key)213     private static void hitKey(int key) {
214         System.out.println("key " + key + " pressed");
215         robot.keyPress(key);
216         robot.keyRelease(key);
217     }
218 
checkSelectionChanged(JTree tree, int shouldBeSel)219     private static boolean checkSelectionChanged(JTree tree, int shouldBeSel) {
220         synchronized(listener) {
221             if (!selectionChanged) {
222                 System.out.println("waiting for selectionChanged...");
223                 try {
224                     listener.wait(5000);
225                 } catch (InterruptedException e) {
226                     e.printStackTrace();
227                 }
228             }
229         }
230         int selRow = tree.getLeadSelectionRow();
231         System.out.println("Selected row: " + selRow);
232         return selRow == shouldBeSel;
233     }
234 
isTreeExpanded()235     private static boolean isTreeExpanded() {
236         synchronized(listener) {
237             if (!treeExpanded) {
238                 System.out.println("waiting for treeExpanded...");
239                 try {
240                     listener.wait(5000);
241                 } catch (InterruptedException e) {
242                     e.printStackTrace();
243                 }
244             }
245         }
246         return treeExpanded;
247     }
248 
isTreeCollapsed()249     private static boolean isTreeCollapsed() {
250         synchronized(listener) {
251             if (!treeCollapsed) {
252                 System.out.println("waiting for treeCollapsed...");
253                 try {
254                     listener.wait(5000);
255                 } catch (InterruptedException e) {
256                     e.printStackTrace();
257                 }
258             }
259         }
260         return treeCollapsed;
261     }
262 }
263