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