1 /* 2 * Copyright (c) 2011, 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertNull; 31 import static org.junit.Assert.assertTrue; 32 33 import java.util.List; 34 35 import com.sun.javafx.scene.control.infrastructure.MouseEventFirer; 36 import com.sun.javafx.tk.Toolkit; 37 import javafx.collections.ListChangeListener; 38 import javafx.scene.Scene; 39 import javafx.scene.layout.VBox; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Test; 44 45 import com.sun.javafx.scene.control.behavior.TreeViewAnchorRetriever; 46 import com.sun.javafx.scene.control.infrastructure.KeyModifier; 47 import com.sun.javafx.scene.control.infrastructure.StageLoader; 48 import com.sun.javafx.scene.control.infrastructure.VirtualFlowTestUtils; 49 50 //@Ignore("Disabling tests as they fail with OOM in continuous builds") 51 public class TreeViewMouseInputTest { 52 private TreeView<String> treeView; 53 private MultipleSelectionModel<TreeItem<String>> sm; 54 private FocusModel<TreeItem<String>> fm; 55 56 private TreeItem<String> root; // 0 57 private TreeItem<String> child1; // 1 58 private TreeItem<String> child2; // 2 59 private TreeItem<String> child3; // 3 60 private TreeItem<String> subchild1; // 4 61 private TreeItem<String> subchild2; // 5 62 private TreeItem<String> subchild3; // 6 63 private TreeItem<String> child4; // 7 64 private TreeItem<String> child5; // 8 65 private TreeItem<String> child6; // 9 66 private TreeItem<String> child7; // 10 67 private TreeItem<String> child8; // 11 68 private TreeItem<String> child9; // 12 69 private TreeItem<String> child10; // 13 70 71 @Before public void setup() { 72 root = new TreeItem<String>("Root"); // 0 73 child1 = new TreeItem<String>("Child 1"); // 1 74 child2 = new TreeItem<String>("Child 2"); // 2 75 child3 = new TreeItem<String>("Child 3"); // 3 76 subchild1 = new TreeItem<String>("Subchild 1"); // 4 77 subchild2 = new TreeItem<String>("Subchild 2"); // 5 78 subchild3 = new TreeItem<String>("Subchild 3"); // 6 79 child4 = new TreeItem<String>("Child 4"); // 7 80 child5 = new TreeItem<String>("Child 5"); // 8 81 child6 = new TreeItem<String>("Child 6"); // 9 82 child7 = new TreeItem<String>("Child 7"); // 10 83 child8 = new TreeItem<String>("Child 8"); // 11 84 child9 = new TreeItem<String>("Child 9"); // 12 85 child10 = new TreeItem<String>("Child 10"); // 13 86 87 // reset tree structure 88 root.getChildren().clear(); 89 root.setExpanded(true); 90 root.getChildren().setAll(child1, child2, child3, child4, child5, child6, child7, child8, child9, child10 ); 91 child1.getChildren().clear(); 92 child1.setExpanded(false); 93 child2.getChildren().clear(); 94 child2.setExpanded(false); 95 child3.getChildren().clear(); 96 child3.setExpanded(true); 97 child3.getChildren().setAll(subchild1, subchild2, subchild3); 98 child4.getChildren().clear(); 99 child4.setExpanded(false); 100 child5.getChildren().clear(); 101 child5.setExpanded(false); 102 child6.getChildren().clear(); 103 child6.setExpanded(false); 104 child7.getChildren().clear(); 105 child7.setExpanded(false); 106 child8.getChildren().clear(); 107 child8.setExpanded(false); 108 child9.getChildren().clear(); 109 child9.setExpanded(false); 110 child10.getChildren().clear(); 111 child10.setExpanded(false); 112 113 // recreate treeview and gather models 114 treeView = new TreeView<String>(); 115 treeView.setRoot(root); 116 sm = treeView.getSelectionModel(); 117 sm.setSelectionMode(SelectionMode.MULTIPLE); 118 fm = treeView.getFocusModel(); 119 } 120 121 @After public void tearDown() { 122 treeView.getSkin().dispose(); 123 } 124 125 126 /*************************************************************************** 127 * Util methods 128 **************************************************************************/ 129 130 private String debug() { 131 StringBuilder sb = new StringBuilder("Selected Indices: ["); 132 133 List<Integer> indices = sm.getSelectedIndices(); 134 for (Integer index : indices) { 135 sb.append(index); 136 sb.append(", "); 137 } 138 139 sb.append("] \nFocus: " + fm.getFocusedIndex()); 140 sb.append(" \nAnchor: " + getAnchor()); 141 return sb.toString(); 142 } 143 144 // Returns true if ALL indices are selected 145 private boolean isSelected(int... indices) { 146 for (int index : indices) { 147 if (! sm.isSelected(index)) return false; 148 } 149 return true; 150 } 151 152 // Returns true if ALL indices are NOT selected 153 private boolean isNotSelected(int... indices) { 154 for (int index : indices) { 155 if (sm.isSelected(index)) return false; 156 } 157 return true; 158 } 159 160 private int getAnchor() { 161 return TreeViewAnchorRetriever.getAnchor(treeView); 162 } 163 164 private boolean isAnchor(int index) { 165 return getAnchor() == index; 166 } 167 168 private int getItemCount() { 169 return root.getChildren().size() + child3.getChildren().size(); 170 } 171 172 173 /*************************************************************************** 174 * Tests for specific bug reports 175 **************************************************************************/ 176 177 @Test public void test_rt29833_mouse_select_upwards() { 178 sm.setSelectionMode(SelectionMode.MULTIPLE); 179 180 sm.clearAndSelect(9); 181 182 // select all from 9 - 7 183 VirtualFlowTestUtils.clickOnRow(treeView, 7, KeyModifier.SHIFT); 184 assertTrue(debug(), isSelected(7,8,9)); 185 186 // select all from 9 - 7 - 5 187 VirtualFlowTestUtils.clickOnRow(treeView, 5, KeyModifier.SHIFT); 188 assertTrue(debug(),isSelected(5,6,7,8,9)); 189 } 190 191 @Test public void test_rt29833_mouse_select_downwards() { 192 sm.setSelectionMode(SelectionMode.MULTIPLE); 193 194 sm.clearAndSelect(5); 195 196 // select all from 5 - 7 197 VirtualFlowTestUtils.clickOnRow(treeView, 7, KeyModifier.SHIFT); 198 assertTrue(debug(), isSelected(5,6,7)); 199 200 // select all from 5 - 7 - 9 201 VirtualFlowTestUtils.clickOnRow(treeView, 9, KeyModifier.SHIFT); 202 assertTrue(debug(),isSelected(5,6,7,8,9)); 203 } 204 205 private int rt30394_count = 0; 206 @Test public void test_rt30394() { 207 sm.setSelectionMode(SelectionMode.MULTIPLE); 208 sm.clearSelection(); 209 210 final FocusModel fm = treeView.getFocusModel(); 211 fm.focus(-1); 212 213 fm.focusedIndexProperty().addListener((observable, oldValue, newValue) -> { 214 rt30394_count++; 215 assertEquals(0, fm.getFocusedIndex()); 216 }); 217 218 // test pre-conditions 219 assertEquals(0,rt30394_count); 220 assertFalse(fm.isFocused(0)); 221 222 // select the first row with the shift key held down. The focus event 223 // should only fire once - for focus on 0 (never -1 as this bug shows). 224 VirtualFlowTestUtils.clickOnRow(treeView, 0, KeyModifier.SHIFT); 225 assertEquals(1, rt30394_count); 226 assertTrue(fm.isFocused(0)); 227 } 228 229 @Test public void test_rt32119() { 230 sm.setSelectionMode(SelectionMode.MULTIPLE); 231 sm.clearSelection(); 232 233 // select rows 2, 3, and 4 234 VirtualFlowTestUtils.clickOnRow(treeView, 2); 235 VirtualFlowTestUtils.clickOnRow(treeView, 4, KeyModifier.SHIFT); 236 assertFalse(sm.isSelected(1)); 237 assertTrue(sm.isSelected(2)); 238 assertTrue(sm.isSelected(3)); 239 assertTrue(sm.isSelected(4)); 240 assertFalse(sm.isSelected(5)); 241 242 // now shift click on the 2nd row - this should make only row 2 be 243 // selected. The bug is that row 4 remains selected also. 244 VirtualFlowTestUtils.clickOnRow(treeView, 2, KeyModifier.SHIFT); 245 assertFalse(sm.isSelected(1)); 246 assertTrue(sm.isSelected(2)); 247 assertFalse(sm.isSelected(3)); 248 assertFalse(sm.isSelected(4)); 249 assertFalse(sm.isSelected(5)); 250 } 251 252 @Test public void test_rt21444_up() { 253 final int items = 8; 254 root.getChildren().clear(); 255 for (int i = 0; i < items; i++) { 256 root.getChildren().add(new TreeItem<>("Row " + i)); 257 } 258 259 final int selectRow = 3; 260 261 treeView.setShowRoot(false); 262 final MultipleSelectionModel<TreeItem<String>> sm = treeView.getSelectionModel(); 263 sm.setSelectionMode(SelectionMode.MULTIPLE); 264 sm.clearAndSelect(selectRow); 265 266 assertEquals(selectRow, sm.getSelectedIndex()); 267 assertEquals("Row 3", sm.getSelectedItem().getValue()); 268 269 VirtualFlowTestUtils.clickOnRow(treeView, selectRow - 1, KeyModifier.SHIFT); 270 assertEquals(2, sm.getSelectedItems().size()); 271 assertEquals("Row 2", sm.getSelectedItem().getValue()); 272 assertEquals("Row 2", sm.getSelectedItems().get(0).getValue()); 273 } 274 275 @Test public void test_rt21444_down() { 276 final int items = 8; 277 root.getChildren().clear(); 278 for (int i = 0; i < items; i++) { 279 root.getChildren().add(new TreeItem<>("Row " + i)); 280 } 281 282 final int selectRow = 3; 283 284 treeView.setShowRoot(false); 285 final MultipleSelectionModel<TreeItem<String>> sm = treeView.getSelectionModel(); 286 sm.setSelectionMode(SelectionMode.MULTIPLE); 287 sm.clearAndSelect(selectRow); 288 289 assertEquals(selectRow, sm.getSelectedIndex()); 290 assertEquals("Row 3", sm.getSelectedItem().getValue()); 291 292 VirtualFlowTestUtils.clickOnRow(treeView, selectRow + 1, KeyModifier.SHIFT); 293 assertEquals(2, sm.getSelectedItems().size()); 294 assertEquals("Row 4", sm.getSelectedItem().getValue()); 295 assertEquals("Row 4", sm.getSelectedItems().get(1).getValue()); 296 } 297 298 @Test public void test_rt32560() { 299 final int items = 8; 300 root.getChildren().clear(); 301 for (int i = 0; i < items; i++) { 302 root.getChildren().add(new TreeItem<>("Row " + i)); 303 } 304 305 final MultipleSelectionModel sm = treeView.getSelectionModel(); 306 sm.setSelectionMode(SelectionMode.MULTIPLE); 307 sm.clearAndSelect(0); 308 309 assertEquals(0, sm.getSelectedIndex()); 310 assertEquals(root, sm.getSelectedItem()); 311 assertEquals(0, fm.getFocusedIndex()); 312 313 VirtualFlowTestUtils.clickOnRow(treeView, 5, KeyModifier.SHIFT); 314 assertEquals(5, sm.getSelectedIndex()); 315 assertEquals(5, fm.getFocusedIndex()); 316 assertEquals(6, sm.getSelectedItems().size()); 317 318 VirtualFlowTestUtils.clickOnRow(treeView, 0, KeyModifier.SHIFT); 319 assertEquals(0, sm.getSelectedIndex()); 320 assertEquals(0, fm.getFocusedIndex()); 321 assertEquals(1, sm.getSelectedItems().size()); 322 } 323 324 @Test public void test_rt_32963() { 325 final int items = 8; 326 root.getChildren().clear(); 327 root.setExpanded(true); 328 for (int i = 0; i < items; i++) { 329 root.getChildren().add(new TreeItem<>("Row " + i)); 330 } 331 treeView.setRoot(root); 332 333 treeView.setShowRoot(true); 334 final MultipleSelectionModel sm = treeView.getSelectionModel(); 335 sm.setSelectionMode(SelectionMode.MULTIPLE); 336 sm.clearAndSelect(0); 337 338 assertEquals(9, treeView.getExpandedItemCount()); 339 340 assertEquals(0, sm.getSelectedIndex()); 341 assertEquals(0, fm.getFocusedIndex()); 342 assertEquals(root, sm.getSelectedItem()); 343 assertEquals(1, sm.getSelectedItems().size()); 344 345 VirtualFlowTestUtils.clickOnRow(treeView, 5, KeyModifier.SHIFT); 346 assertEquals("Actual selected index: " + sm.getSelectedIndex(), 5, sm.getSelectedIndex()); 347 assertEquals("Actual focused index: " + fm.getFocusedIndex(), 5, fm.getFocusedIndex()); 348 assertTrue("Selected indices: " + sm.getSelectedIndices(), sm.getSelectedIndices().contains(0)); 349 assertTrue("Selected items: " + sm.getSelectedItems(), sm.getSelectedItems().contains(root)); 350 assertEquals(6, sm.getSelectedItems().size()); 351 } 352 353 private int rt_30626_count = 0; 354 @Test public void test_rt_30626() { 355 final int items = 8; 356 root.getChildren().clear(); 357 root.setExpanded(true); 358 for (int i = 0; i < items; i++) { 359 root.getChildren().add(new TreeItem<>("Row " + i)); 360 } 361 treeView.setRoot(root); 362 363 treeView.setShowRoot(true); 364 final MultipleSelectionModel sm = treeView.getSelectionModel(); 365 sm.setSelectionMode(SelectionMode.MULTIPLE); 366 sm.clearAndSelect(0); 367 368 treeView.getSelectionModel().getSelectedItems().addListener((ListChangeListener) c -> { 369 while (c.next()) { 370 rt_30626_count++; 371 } 372 }); 373 374 assertEquals(0, rt_30626_count); 375 VirtualFlowTestUtils.clickOnRow(treeView, 1); 376 assertEquals(1, rt_30626_count); 377 378 VirtualFlowTestUtils.clickOnRow(treeView, 1); 379 assertEquals(1, rt_30626_count); 380 } 381 382 @Test public void test_rt_34649() { 383 final int items = 8; 384 root.getChildren().clear(); 385 root.setExpanded(true); 386 for (int i = 0; i < items; i++) { 387 root.getChildren().add(new TreeItem<>("Row " + i)); 388 } 389 treeView.setRoot(root); 390 391 final MultipleSelectionModel sm = treeView.getSelectionModel(); 392 final FocusModel fm = treeView.getFocusModel(); 393 sm.setSelectionMode(SelectionMode.SINGLE); 394 395 assertFalse(sm.isSelected(4)); 396 assertFalse(fm.isFocused(4)); 397 VirtualFlowTestUtils.clickOnRow(treeView, 4, KeyModifier.getShortcutKey()); 398 assertTrue(sm.isSelected(4)); 399 assertTrue(fm.isFocused(4)); 400 401 VirtualFlowTestUtils.clickOnRow(treeView, 4, KeyModifier.getShortcutKey()); 402 assertFalse(sm.isSelected(4)); 403 assertTrue(fm.isFocused(4)); 404 } 405 406 @Test public void test_rt_36509() { 407 final int items = 8; 408 root.getChildren().clear(); 409 root.setExpanded(false); 410 for (int i = 0; i < items; i++) { 411 root.getChildren().add(new TreeItem<>("Row " + i)); 412 } 413 treeView.setRoot(root); 414 415 // expand 416 assertFalse(root.isExpanded()); 417 VirtualFlowTestUtils.clickOnRow(treeView, 0, 2); 418 assertTrue(root.isExpanded()); 419 420 // collapse 421 VirtualFlowTestUtils.clickOnRow(treeView, 0, 2); 422 assertFalse(root.isExpanded()); 423 424 // expand again 425 VirtualFlowTestUtils.clickOnRow(treeView, 0, 2); 426 assertTrue(root.isExpanded()); 427 } 428 429 @Test public void test_rt_37069() { 430 final int items = 8; 431 root.getChildren().clear(); 432 root.setExpanded(false); 433 for (int i = 0; i < items; i++) { 434 root.getChildren().add(new TreeItem<>("Row " + i)); 435 } 436 treeView.setRoot(root); 437 treeView.setFocusTraversable(false); 438 439 Button btn = new Button("Button"); 440 VBox vbox = new VBox(btn, treeView); 441 442 StageLoader sl = new StageLoader(vbox); 443 sl.getStage().requestFocus(); 444 btn.requestFocus(); 445 Toolkit.getToolkit().firePulse(); 446 Scene scene = sl.getStage().getScene(); 447 448 assertTrue(btn.isFocused()); 449 assertFalse(treeView.isFocused()); 450 451 ScrollBar vbar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(treeView); 452 MouseEventFirer mouse = new MouseEventFirer(vbar); 453 mouse.fireMousePressAndRelease(); 454 455 assertTrue(btn.isFocused()); 456 assertFalse(treeView.isFocused()); 457 458 sl.dispose(); 459 } 460 }