1/*
2 * Copyright (c) 2011, 2012, 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 6263446
28 * @summary Tests that double-clicking to edit a cell doesn't select the content.
29 * @author Shannon Hickey
30 * @modules java.desktop/javax.swing.tree:open
31 * @run main bug6263446
32 */
33import java.awt.*;
34import java.awt.event.InputEvent;
35import java.lang.reflect.Field;
36import javax.swing.*;
37import javax.swing.tree.*;
38
39public class bug6263446 {
40
41    private static final String FIRST = "AAAAAAAAAAA";
42    private static final String SECOND = "BB";
43    private static final String ALL = FIRST + " " + SECOND;
44    private static JTree tree;
45    private static Robot robot;
46
47    public static void main(String[] args) throws Exception {
48        robot = new Robot();
49        robot.setAutoDelay(50);
50
51        SwingUtilities.invokeAndWait(new Runnable() {
52
53            public void run() {
54                createAndShowGUI();
55            }
56        });
57
58        robot.waitForIdle();
59
60        Point point = getClickPoint();
61        robot.mouseMove(point.x, point.y);
62
63        // click count 3
64        click(1);
65        assertNotEditing();
66
67        click(2);
68        assertNotEditing();
69
70        click(3);
71        assertEditing();
72        cancelCellEditing();
73        assertNotEditing();
74
75        click(4);
76        checkSelectedText(FIRST);
77
78        click(5);
79        checkSelectedText(ALL);
80
81        // click count 4
82        setClickCountToStart(4);
83
84        click(1);
85        assertNotEditing();
86
87        click(2);
88        assertNotEditing();
89
90        click(3);
91        assertNotEditing();
92
93        click(4);
94        assertEditing();
95        cancelCellEditing();
96        assertNotEditing();
97
98        click(5);
99        checkSelectedText(FIRST);
100
101        click(6);
102        checkSelectedText(ALL);
103
104        // start path editing
105        startPathEditing();
106        assertEditing();
107
108        click(1);
109        checkSelection(null);
110
111        click(2);
112        checkSelection(FIRST);
113
114        click(3);
115        checkSelection(ALL);
116    }
117
118    private static void click(int times) {
119        robot.delay(500);
120        for (int i = 0; i < times; i++) {
121            robot.mousePress(InputEvent.BUTTON1_MASK);
122            robot.mouseRelease(InputEvent.BUTTON1_MASK);
123        }
124    }
125
126    private static Point getClickPoint() throws Exception {
127        final Point[] result = new Point[1];
128
129        SwingUtilities.invokeAndWait(new Runnable() {
130
131            @Override
132            public void run() {
133                Rectangle rect = tree.getRowBounds(0);
134                // UPDATE !!!
135                Point p = new Point(rect.x + rect.width / 2, rect.y + 2);
136                SwingUtilities.convertPointToScreen(p, tree);
137                result[0] = p;
138
139            }
140        });
141
142        return result[0];
143    }
144
145    private static TreeModel createTreeModel() {
146        return new DefaultTreeModel(new DefaultMutableTreeNode(ALL));
147    }
148
149    private static void createAndShowGUI() {
150
151        JFrame frame = new JFrame();
152        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
153
154        tree = new JTree(createTreeModel());
155        tree.setRootVisible(true);
156        tree.setEditable(true);
157
158
159        frame.getContentPane().add(tree);
160        frame.pack();
161        frame.setVisible(true);
162    }
163
164    private static void setClickCountToStart(final int clicks) throws Exception {
165        SwingUtilities.invokeAndWait(new Runnable() {
166
167            @Override
168            public void run() {
169                try {
170                    DefaultTreeCellEditor editor =
171                            (DefaultTreeCellEditor) tree.getCellEditor();
172                    Field field = DefaultTreeCellEditor.class.getDeclaredField("realEditor");
173                    field.setAccessible(true);
174                    DefaultCellEditor ce = (DefaultCellEditor) field.get(editor);
175                    ce.setClickCountToStart(clicks);
176                } catch (IllegalAccessException e) {
177                    throw new RuntimeException(e);
178                } catch (NoSuchFieldException e) {
179                    throw new RuntimeException(e);
180                }
181            }
182        });
183
184        robot.waitForIdle();
185
186    }
187
188    private static void startPathEditing() throws Exception {
189        SwingUtilities.invokeAndWait(new Runnable() {
190
191            @Override
192            public void run() {
193                tree.startEditingAtPath(tree.getPathForRow(0));
194            }
195        });
196    }
197
198    private static void cancelCellEditing() throws Exception {
199        SwingUtilities.invokeAndWait(new Runnable() {
200
201            @Override
202            public void run() {
203                tree.getCellEditor().cancelCellEditing();
204            }
205        });
206    }
207
208    private static void checkSelection(final String sel) throws Exception {
209        SwingUtilities.invokeAndWait(new Runnable() {
210
211            @Override
212            public void run() {
213                try {
214                    DefaultTreeCellEditor editor =
215                            (DefaultTreeCellEditor) tree.getCellEditor();
216                    Field field = DefaultTreeCellEditor.class.getDeclaredField("realEditor");
217                    field.setAccessible(true);
218                    DefaultCellEditor ce = (DefaultCellEditor) field.get(editor);
219                    JTextField tf = (JTextField) ce.getComponent();
220                    String text = tf.getSelectedText();
221
222                    if (sel == null) {
223                        if (text != null && text.length() != 0) {
224                            throw new RuntimeException("Nothing should be selected, but \"" + text + "\" is selected.");
225                        }
226                    } else if (!sel.equals(text)) {
227                        throw new RuntimeException("\"" + sel + "\" should be selected, but \"" + text + "\" is selected.");
228                    }
229                } catch (IllegalAccessException e) {
230                    throw new RuntimeException(e);
231                } catch (NoSuchFieldException e) {
232                    throw new RuntimeException(e);
233                }
234            }
235        });
236    }
237
238    private static void checkSelectedText(String sel) throws Exception {
239        assertEditing();
240        checkSelection(sel);
241        cancelCellEditing();
242        assertNotEditing();
243    }
244
245    private static void assertEditing() throws Exception {
246        assertEditingNoTreeLock(true);
247    }
248
249    private static void assertNotEditing() throws Exception {
250        assertEditingNoTreeLock(false);
251    }
252
253    private static void assertEditingNoTreeLock(final boolean editing) throws Exception {
254        robot.waitForIdle();
255
256        SwingUtilities.invokeAndWait(new Runnable() {
257
258            @Override
259            public void run() {
260                if (editing && !tree.isEditing()) {
261                    throw new RuntimeException("Tree should be editing");
262                }
263                if (!editing && tree.isEditing()) {
264                    throw new RuntimeException("Tree should not be editing");
265                }
266            }
267        });
268
269    }
270
271}
272