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 com.sun.javafx.PlatformUtil;
  29 import com.sun.javafx.util.Utils;
  30 import com.sun.javafx.scene.control.behavior.TreeTableViewAnchorRetriever;
  31 import com.sun.javafx.scene.control.infrastructure.*;
  32 import com.sun.javafx.scene.control.skin.TreeTableViewSkin;
  33 import com.sun.javafx.scene.control.test.Person;
  34 
  35 import static org.junit.Assert.*;
  36 
  37 import java.util.List;
  38 
  39 import com.sun.javafx.tk.Toolkit;
  40 import javafx.beans.value.ChangeListener;
  41 import javafx.beans.value.ObservableValue;
  42 import javafx.collections.FXCollections;
  43 import javafx.collections.ListChangeListener;
  44 import javafx.collections.ObservableList;
  45 import javafx.scene.Group;
  46 import javafx.scene.Node;
  47 import javafx.scene.Scene;
  48 import javafx.scene.control.cell.PropertyValueFactory;
  49 import javafx.scene.control.cell.TreeItemPropertyValueFactory;
  50 import javafx.scene.input.KeyCode;
  51 
  52 import javafx.scene.layout.StackPane;
  53 import javafx.scene.layout.VBox;
  54 import javafx.stage.Stage;
  55 import org.junit.After;
  56 
  57 import org.junit.Before;
  58 import org.junit.Ignore;
  59 import org.junit.Test;
  60 
  61 //@Ignore("Disabling tests as they fail with OOM in continuous builds")
  62 public class TreeTableViewMouseInputTest {
  63     private TreeTableView<String> tableView;
  64     private TreeTableView.TreeTableViewSelectionModel<?> sm;
  65     private TreeTableView.TreeTableViewFocusModel<?> fm;
  66     
  67     private final TreeTableColumn<String, String> col0 = new TreeTableColumn<String, String>("col0");
  68     private final TreeTableColumn<String, String> col1 = new TreeTableColumn<String, String>("col1");
  69     private final TreeTableColumn<String, String> col2 = new TreeTableColumn<String, String>("col2");
  70     private final TreeTableColumn<String, String> col3 = new TreeTableColumn<String, String>("col3");
  71     private final TreeTableColumn<String, String> col4 = new TreeTableColumn<String, String>("col4");
  72 
  73     private TreeItem<String> root;                  // 0
  74     private TreeItem<String> child1;            // 1
  75     private TreeItem<String> child2;            // 2
  76     private TreeItem<String> child3;            // 3
  77     private TreeItem<String> subchild1;     // 4
  78     private TreeItem<String> subchild2;     // 5
  79     private TreeItem<String> subchild3;     // 6
  80     private TreeItem<String> child4;            // 7
  81     private TreeItem<String> child5;            // 8
  82     private TreeItem<String> child6;            // 9
  83     private TreeItem<String> child7;            // 10
  84     private TreeItem<String> child8;            // 11
  85     private TreeItem<String> child9;            // 12
  86     private TreeItem<String> child10;           // 13
  87 
  88     @Before public void setup() {
  89         root = new TreeItem<String>("Root");             // 0
  90         child1 = new TreeItem<String>("Child 1");        // 1
  91         child2 = new TreeItem<String>("Child 2");        // 2
  92         child3 = new TreeItem<String>("Child 3");        // 3
  93         subchild1 = new TreeItem<String>("Subchild 1");  // 4
  94         subchild2 = new TreeItem<String>("Subchild 2");  // 5
  95         subchild3 = new TreeItem<String>("Subchild 3");  // 6
  96         child4 = new TreeItem<String>("Child 4");        // 7
  97         child5 = new TreeItem<String>("Child 5");        // 8
  98         child6 = new TreeItem<String>("Child 6");        // 9
  99         child7 = new TreeItem<String>("Child 7");        // 10
 100         child8 = new TreeItem<String>("Child 8");        // 11
 101         child9 = new TreeItem<String>("Child 9");        // 12
 102         child10 = new TreeItem<String>("Child 10");      // 13
 103 
 104         // reset tree structure
 105         root.getChildren().clear();
 106         root.setExpanded(true);
 107         root.getChildren().setAll(child1, child2, child3, child4, child5, child6, child7, child8, child9, child10 );
 108         child1.getChildren().clear();
 109         child1.setExpanded(false);
 110         child2.getChildren().clear();
 111         child2.setExpanded(false);
 112         child3.getChildren().clear();
 113         child3.setExpanded(true);
 114         child3.getChildren().setAll(subchild1, subchild2, subchild3);
 115         child4.getChildren().clear();
 116         child4.setExpanded(false);
 117         child5.getChildren().clear();
 118         child5.setExpanded(false);
 119         child6.getChildren().clear();
 120         child6.setExpanded(false);
 121         child7.getChildren().clear();
 122         child7.setExpanded(false);
 123         child8.getChildren().clear();
 124         child8.setExpanded(false);
 125         child9.getChildren().clear();
 126         child9.setExpanded(false);
 127         child10.getChildren().clear();
 128         child10.setExpanded(false);
 129         
 130         tableView = new TreeTableView<String>();
 131         sm = tableView.getSelectionModel();
 132         fm = tableView.getFocusModel();
 133         
 134         sm.setSelectionMode(SelectionMode.MULTIPLE);
 135         sm.setCellSelectionEnabled(false);
 136         
 137         tableView.setRoot(root);
 138         tableView.getColumns().setAll(col0, col1, col2, col3, col4);
 139     }
 140     
 141     @After public void tearDown() {
 142         if (tableView.getSkin() != null) {
 143             tableView.getSkin().dispose();
 144         }
 145         sm = null;
 146     }
 147     
 148     /***************************************************************************
 149      * Util methods
 150      **************************************************************************/
 151     
 152     private String debug() {
 153         StringBuilder sb = new StringBuilder("Selected Cells: [");
 154         
 155         ObservableList<? extends TreeTablePosition<?, ?>> cells = sm.getSelectedCells();
 156         for (TreeTablePosition<?,?> tp : cells) {
 157             sb.append("(");
 158             sb.append(tp.getRow());
 159             sb.append(",");
 160             sb.append(tp.getColumn());
 161             sb.append("), ");
 162         }
 163         
 164         sb.append("] \nFocus: (" + fm.getFocusedCell().getRow() + ", " + fm.getFocusedCell().getColumn() + ")");
 165         
 166         TreeTablePosition anchor = getAnchor();
 167         sb.append(" \nAnchor: (" + (anchor == null ? "null" : anchor.getRow()) + 
 168                 ", " + (anchor == null ? "null" : anchor.getColumn()) + ")");
 169         return sb.toString();
 170     }
 171     
 172     // Returns true if ALL indices are selected
 173     private boolean isSelected(int... indices) {
 174         for (int index : indices) {
 175             if (! sm.isSelected(index)) return false;
 176         }
 177         return true;
 178     }
 179     
 180     // Returns true if ALL indices are NOT selected
 181     private boolean isNotSelected(int... indices) {
 182         for (int index : indices) {
 183             if (sm.isSelected(index)) return false;
 184         }
 185         return true;
 186     }
 187     
 188     private TreeTablePosition getAnchor() {
 189         return TreeTableViewAnchorRetriever.getAnchor(tableView);
 190     }
 191     
 192     private boolean isAnchor(int row) {
 193         TreeTablePosition tp = new TreeTablePosition(tableView, row, null);
 194         return getAnchor() != null && getAnchor().equals(tp);
 195     }
 196     
 197     private boolean isAnchor(int row, int col) {
 198         TreeTablePosition tp = new TreeTablePosition(tableView, row, tableView.getColumns().get(col));
 199         return getAnchor() != null && getAnchor().equals(tp);
 200     }
 201     
 202     private int getItemCount() {
 203         return root.getChildren().size() + child3.getChildren().size();
 204     }
 205     
 206     
 207     
 208     /***************************************************************************
 209      * Tests for specific bug reports
 210      **************************************************************************/
 211     
 212     @Test public void test_rt29833_mouse_select_upwards() {
 213         sm.setCellSelectionEnabled(false);
 214         sm.setSelectionMode(SelectionMode.MULTIPLE);
 215         
 216         sm.clearAndSelect(9);
 217         
 218         // select all from 9 - 7
 219         VirtualFlowTestUtils.clickOnRow(tableView, 7, KeyModifier.SHIFT);
 220         assertTrue(debug(), isSelected(7,8,9));
 221         
 222         // select all from 9 - 7 - 5
 223         VirtualFlowTestUtils.clickOnRow(tableView, 5, KeyModifier.SHIFT);
 224         assertTrue(debug(),isSelected(5,6,7,8,9));
 225     }
 226     
 227     @Test public void test_rt29833_mouse_select_downwards() {
 228         sm.setCellSelectionEnabled(false);
 229         sm.setSelectionMode(SelectionMode.MULTIPLE);
 230         
 231         sm.clearAndSelect(5);
 232         
 233         // select all from 5 - 7
 234         VirtualFlowTestUtils.clickOnRow(tableView, 7, KeyModifier.SHIFT);
 235         assertTrue(debug(), isSelected(5,6,7));
 236         
 237         // select all from 5 - 7 - 9
 238         VirtualFlowTestUtils.clickOnRow(tableView, 9, KeyModifier.SHIFT);
 239         assertTrue(debug(),isSelected(5,6,7,8,9));
 240     }
 241 
 242     private int rt30394_count = 0;
 243 //    @Ignore("Ignoring due to RT-37166")
 244     @Test public void test_rt30394() {
 245         sm.setCellSelectionEnabled(false);
 246         sm.setSelectionMode(SelectionMode.MULTIPLE);
 247         sm.clearSelection();
 248 
 249         final TableFocusModel fm = tableView.getFocusModel();
 250         fm.focus(-1);
 251 
 252         fm.focusedIndexProperty().addListener((observable, oldValue, newValue) -> {
 253             rt30394_count++;
 254             assertEquals(0, fm.getFocusedIndex());
 255         });
 256 
 257         // test pre-conditions
 258         assertEquals(0,rt30394_count);
 259         assertFalse(fm.isFocused(0));
 260 
 261         // select the first row with the shift key held down. The focus event
 262         // should only fire once - for focus on 0 (never -1 as this bug shows).
 263         VirtualFlowTestUtils.clickOnRow(tableView, 0, KeyModifier.SHIFT);
 264         Toolkit.getToolkit().firePulse();
 265         assertEquals(1, rt30394_count);
 266         assertTrue(fm.isFocused(0));
 267     }
 268 
 269     @Test public void test_rt32119() {
 270         sm.setSelectionMode(SelectionMode.MULTIPLE);
 271         sm.clearSelection();
 272 
 273         // select rows 2, 3, and 4
 274         VirtualFlowTestUtils.clickOnRow(tableView, 2);
 275         VirtualFlowTestUtils.clickOnRow(tableView, 4, KeyModifier.SHIFT);
 276         assertFalse(sm.isSelected(1));
 277         assertTrue(sm.isSelected(2));
 278         assertTrue(sm.isSelected(3));
 279         assertTrue(sm.isSelected(4));
 280         assertFalse(sm.isSelected(5));
 281 
 282         // now shift click on the 2nd row - this should make only row 2 be
 283         // selected. The bug is that row 4 remains selected also.
 284         VirtualFlowTestUtils.clickOnRow(tableView, 2, KeyModifier.SHIFT);
 285         assertFalse(sm.isSelected(1));
 286         assertTrue(sm.isSelected(2));
 287         assertFalse(sm.isSelected(3));
 288         assertFalse(sm.isSelected(4));
 289         assertFalse(sm.isSelected(5));
 290     }
 291 
 292     @Test public void test_rt31020() {
 293         sm.setSelectionMode(SelectionMode.MULTIPLE);
 294         sm.clearSelection();
 295 
 296         // set all the columns to be very narrow (so the mouse click happens
 297         // to the right of them all, out in no-mans land
 298         tableView.setMinWidth(200);
 299         tableView.setPrefWidth(200);
 300         tableView.getColumns().clear();
 301         col0.setMaxWidth(10);
 302         tableView.getColumns().add(col0);
 303 
 304         // select rows 1, 2, 3, 4, and 5
 305         VirtualFlowTestUtils.clickOnRow(tableView, 1, true);
 306         VirtualFlowTestUtils.clickOnRow(tableView, 5, true, KeyModifier.SHIFT);
 307         assertTrue(sm.isSelected(1));
 308         assertTrue(sm.isSelected(2));
 309         assertTrue(sm.isSelected(3));
 310         assertTrue(sm.isSelected(4));
 311         assertTrue(sm.isSelected(5));
 312     }
 313 
 314     @Test public void test_rt21444_up_cell() {
 315         final int items = 8;
 316         root.getChildren().clear();
 317         for (int i = 0; i < items; i++) {
 318             root.getChildren().add(new TreeItem<>("Row " + i));
 319         }
 320 
 321         final int selectRow = 3;
 322 
 323         tableView.setShowRoot(false);
 324         final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
 325         sm.setSelectionMode(SelectionMode.MULTIPLE);
 326         sm.clearAndSelect(selectRow);
 327 
 328         assertEquals(selectRow, sm.getSelectedIndex());
 329         assertEquals("Row 3", sm.getSelectedItem().getValue());
 330 
 331         VirtualFlowTestUtils.clickOnRow(tableView, selectRow - 1, KeyModifier.SHIFT);
 332         assertEquals(2, sm.getSelectedItems().size());
 333         assertEquals("Row 2", sm.getSelectedItem().getValue());
 334         assertEquals("Row 2", sm.getSelectedItems().get(0).getValue());
 335     }
 336 
 337     @Test public void test_rt21444_down_cell() {
 338         final int items = 8;
 339         root.getChildren().clear();
 340         for (int i = 0; i < items; i++) {
 341             root.getChildren().add(new TreeItem<>("Row " + i));
 342         }
 343 
 344         final int selectRow = 3;
 345 
 346         tableView.setShowRoot(false);
 347         final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
 348         sm.setSelectionMode(SelectionMode.MULTIPLE);
 349         sm.clearAndSelect(selectRow);
 350 
 351         assertEquals(selectRow, sm.getSelectedIndex());
 352         assertEquals("Row 3", sm.getSelectedItem().getValue());
 353 
 354         VirtualFlowTestUtils.clickOnRow(tableView, selectRow + 1, KeyModifier.SHIFT);
 355         assertEquals("Row 4", sm.getSelectedItem().getValue());
 356     }
 357 
 358     @Test public void test_rt21444_up_row() {
 359         final int items = 8;
 360         root.getChildren().clear();
 361         for (int i = 0; i < items; i++) {
 362             root.getChildren().add(new TreeItem<>("Row " + i));
 363         }
 364 
 365         final int selectRow = 3;
 366 
 367         tableView.setShowRoot(false);
 368         final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
 369         sm.setSelectionMode(SelectionMode.MULTIPLE);
 370         sm.clearAndSelect(selectRow);
 371 
 372         assertEquals(selectRow, sm.getSelectedIndex());
 373         assertEquals("Row 3", sm.getSelectedItem().getValue());
 374 
 375         VirtualFlowTestUtils.clickOnRow(tableView, selectRow - 1, true, KeyModifier.SHIFT);
 376         assertEquals(2, sm.getSelectedItems().size());
 377         assertEquals("Row 2", sm.getSelectedItem().getValue());
 378         assertEquals("Row 2", sm.getSelectedItems().get(0).getValue());
 379     }
 380 
 381     @Test public void test_rt21444_down_row() {
 382         final int items = 8;
 383         root.getChildren().clear();
 384         for (int i = 0; i < items; i++) {
 385             root.getChildren().add(new TreeItem<>("Row " + i));
 386         }
 387 
 388         final int selectRow = 3;
 389 
 390         tableView.setShowRoot(false);
 391         final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
 392         sm.setSelectionMode(SelectionMode.MULTIPLE);
 393         sm.clearAndSelect(selectRow);
 394 
 395         assertEquals(selectRow, sm.getSelectedIndex());
 396         assertEquals("Row 3", sm.getSelectedItem().getValue());
 397 
 398         VirtualFlowTestUtils.clickOnRow(tableView, selectRow + 1, true, KeyModifier.SHIFT);
 399         assertEquals("Row 4", sm.getSelectedItem().getValue());
 400     }
 401 
 402     @Test public void test_rt32560_cell() {
 403         final int items = 8;
 404         root.getChildren().clear();
 405         for (int i = 0; i < items; i++) {
 406             root.getChildren().add(new TreeItem<>("Row " + i));
 407         }
 408 
 409         final MultipleSelectionModel sm = tableView.getSelectionModel();
 410         sm.setSelectionMode(SelectionMode.MULTIPLE);
 411         sm.clearAndSelect(0);
 412 
 413         assertEquals(0, sm.getSelectedIndex());
 414         assertEquals(root, sm.getSelectedItem());
 415         assertEquals(0, fm.getFocusedIndex());
 416 
 417         VirtualFlowTestUtils.clickOnRow(tableView, 5, KeyModifier.SHIFT);
 418         assertEquals(5, sm.getSelectedIndex());
 419         assertEquals(5, fm.getFocusedIndex());
 420         assertEquals(6, sm.getSelectedItems().size());
 421 
 422         VirtualFlowTestUtils.clickOnRow(tableView, 0, KeyModifier.SHIFT);
 423         assertEquals(0, sm.getSelectedIndex());
 424         assertEquals(0, fm.getFocusedIndex());
 425         assertEquals(1, sm.getSelectedItems().size());
 426     }
 427 
 428     @Test public void test_rt32560_row() {
 429         final int items = 8;
 430         root.getChildren().clear();
 431         for (int i = 0; i < items; i++) {
 432             root.getChildren().add(new TreeItem<>("Row " + i));
 433         }
 434 
 435 //        StageLoader sl = new StageLoader(tableView);
 436 
 437         tableView.setShowRoot(true);
 438         final MultipleSelectionModel sm = tableView.getSelectionModel();
 439         sm.setSelectionMode(SelectionMode.MULTIPLE);
 440         sm.clearAndSelect(0);
 441 
 442         assertEquals(0, sm.getSelectedIndex());
 443         assertEquals(root, sm.getSelectedItem());
 444         assertEquals(0, fm.getFocusedIndex());
 445 
 446         VirtualFlowTestUtils.clickOnRow(tableView, 5, true, KeyModifier.SHIFT);
 447         assertEquals(5, sm.getSelectedIndex());
 448         assertEquals(5, fm.getFocusedIndex());
 449         assertEquals(sm.getSelectedItems().toString(), 6, sm.getSelectedItems().size());
 450 
 451         VirtualFlowTestUtils.clickOnRow(tableView, 0, true, KeyModifier.SHIFT);
 452         assertEquals(0, sm.getSelectedIndex());
 453         assertEquals(0, fm.getFocusedIndex());
 454         assertEquals(debug(), 1, sm.getSelectedItems().size());
 455 
 456 //        sl.dispose();
 457     }
 458 
 459     @Test public void test_rt_32963() {
 460         final int items = 8;
 461         root.getChildren().clear();
 462         root.setExpanded(true);
 463         for (int i = 0; i < items; i++) {
 464             root.getChildren().add(new TreeItem<>("Row " + i));
 465         }
 466         tableView.setRoot(root);
 467 
 468         tableView.setShowRoot(true);
 469         final MultipleSelectionModel sm = tableView.getSelectionModel();
 470         sm.setSelectionMode(SelectionMode.MULTIPLE);
 471         sm.clearAndSelect(0);
 472 
 473         assertEquals(9, tableView.getExpandedItemCount());
 474 
 475         assertEquals(0, sm.getSelectedIndex());
 476         assertEquals(0, fm.getFocusedIndex());
 477         assertEquals(root, sm.getSelectedItem());
 478         assertEquals(1, sm.getSelectedItems().size());
 479 
 480         VirtualFlowTestUtils.clickOnRow(tableView, 5, KeyModifier.SHIFT);
 481         assertEquals("Actual selected index: " + sm.getSelectedIndex(), 5, sm.getSelectedIndex());
 482         assertEquals("Actual focused index: " + fm.getFocusedIndex(), 5, fm.getFocusedIndex());
 483         assertTrue("Selected indices: " + sm.getSelectedIndices(), sm.getSelectedIndices().contains(0));
 484         assertTrue("Selected items: " + sm.getSelectedItems(), sm.getSelectedItems().contains(root));
 485         assertEquals(6, sm.getSelectedItems().size());
 486     }
 487 
 488     @Ignore("Test doesn't work - mouse event not firing as expected")
 489     @Test public void test_rt_33101() {
 490         final int items = 8;
 491         root.setValue("New Root");
 492         root.getChildren().clear();
 493         root.setExpanded(true);
 494         for (int i = 0; i < items; i++) {
 495             root.getChildren().add(new TreeItem<>("Row " + i));
 496         }
 497         tableView.setRoot(root);
 498 
 499         StageLoader sl = new StageLoader(tableView);
 500 
 501         tableView.setShowRoot(true);
 502         final MultipleSelectionModel sm = tableView.getSelectionModel();
 503         sm.setSelectionMode(SelectionMode.MULTIPLE);
 504         sm.clearAndSelect(0);
 505 
 506         TreeTableRow rootRow = (TreeTableRow) VirtualFlowTestUtils.getCell(tableView, 0);
 507         Node disclosureNode = rootRow.getDisclosureNode();
 508 
 509         assertTrue(root.isExpanded());
 510         assertEquals("New Root", rootRow.getTreeItem().getValue());
 511         assertNotNull(disclosureNode);
 512         assertTrue(disclosureNode.isVisible());
 513         assertTrue(disclosureNode.getScene() != null);
 514         assertEquals(9, tableView.getExpandedItemCount());
 515 
 516         MouseEventFirer mouse = new MouseEventFirer(disclosureNode);
 517         mouse.fireMousePressAndRelease();
 518         assertFalse(root.isExpanded());
 519         assertEquals(1, tableView.getExpandedItemCount());
 520 
 521         mouse.fireMousePressAndRelease();
 522         assertTrue(root.isExpanded());
 523         assertEquals(9, tableView.getExpandedItemCount());
 524 
 525         mouse.dispose();
 526         sl.dispose();
 527     }
 528 
 529     private int rt_30626_count = 0;
 530     @Test public void test_rt_30626() {
 531         final int items = 8;
 532         root.getChildren().clear();
 533         root.setExpanded(true);
 534         for (int i = 1; i < items; i++) {
 535             root.getChildren().add(new TreeItem<>("Row " + i));
 536         }
 537         tableView.setRoot(root);
 538 
 539         tableView.setShowRoot(true);
 540         final MultipleSelectionModel sm = tableView.getSelectionModel();
 541         sm.setSelectionMode(SelectionMode.MULTIPLE);
 542         sm.clearAndSelect(0);
 543 
 544         tableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener) c -> {
 545             while (c.next()) {
 546                 rt_30626_count++;
 547             }
 548         });
 549 
 550         Cell cell = VirtualFlowTestUtils.getCell(tableView, 1, 0);
 551         MouseEventFirer mouse = new MouseEventFirer(cell);
 552 
 553         assertEquals(0, rt_30626_count);
 554         mouse.fireMousePressAndRelease();
 555         assertEquals(1, rt_30626_count);
 556 
 557         mouse.fireMousePressAndRelease();
 558         assertEquals(1, rt_30626_count);
 559     }
 560 
 561     @Test public void test_rt_33897_rowSelection() {
 562         final int items = 8;
 563         root.getChildren().clear();
 564         root.setExpanded(true);
 565         for (int i = 0; i < items; i++) {
 566             root.getChildren().add(new TreeItem<>("Row " + i));
 567         }
 568         tableView.setRoot(root);
 569 
 570         final TreeTableView.TreeTableViewSelectionModel<String> sm = tableView.getSelectionModel();
 571         sm.setSelectionMode(SelectionMode.MULTIPLE);
 572         sm.setCellSelectionEnabled(false);
 573 
 574         VirtualFlowTestUtils.clickOnRow(tableView, 1);
 575         assertEquals(1, sm.getSelectedCells().size());
 576 
 577         TreeTablePosition pos = sm.getSelectedCells().get(0);
 578         assertEquals(1, pos.getRow());
 579         assertNotNull(pos.getTableColumn());
 580     }
 581 
 582     @Test public void test_rt_33897_cellSelection() {
 583         final int items = 8;
 584         root.getChildren().clear();
 585         root.setExpanded(true);
 586         for (int i = 0; i < items; i++) {
 587             root.getChildren().add(new TreeItem<>("Row " + i));
 588         }
 589         tableView.setRoot(root);
 590 
 591         final TreeTableView.TreeTableViewSelectionModel<String> sm = tableView.getSelectionModel();
 592         sm.setSelectionMode(SelectionMode.MULTIPLE);
 593         sm.setCellSelectionEnabled(true);
 594 
 595         VirtualFlowTestUtils.clickOnRow(tableView, 1);
 596         assertEquals(1, sm.getSelectedCells().size());
 597 
 598         TreeTablePosition pos = sm.getSelectedCells().get(0);
 599         assertEquals(1, pos.getRow());
 600         assertNotNull(pos.getTableColumn());
 601     }
 602 
 603     @Test public void test_rt_34649() {
 604         final int items = 8;
 605         root.getChildren().clear();
 606         root.setExpanded(true);
 607         for (int i = 0; i < items; i++) {
 608             root.getChildren().add(new TreeItem<>("Row " + i));
 609         }
 610         tableView.setRoot(root);
 611 
 612         final MultipleSelectionModel sm = tableView.getSelectionModel();
 613         final FocusModel fm = tableView.getFocusModel();
 614         sm.setSelectionMode(SelectionMode.SINGLE);
 615 
 616         assertFalse(sm.isSelected(4));
 617         assertFalse(fm.isFocused(4));
 618         VirtualFlowTestUtils.clickOnRow(tableView, 4, true, KeyModifier.getShortcutKey());
 619         assertTrue(sm.isSelected(4));
 620         assertTrue(fm.isFocused(4));
 621 
 622         VirtualFlowTestUtils.clickOnRow(tableView, 4, true, KeyModifier.getShortcutKey());
 623         assertFalse(sm.isSelected(4));
 624         assertTrue(fm.isFocused(4));
 625     }
 626 
 627     @Test public void test_rt_35338() {
 628         root.getChildren().clear();
 629         tableView.setRoot(root);
 630         tableView.getColumns().setAll(col0);
 631 
 632         col0.setWidth(20);
 633         tableView.setMinWidth(1000);
 634         tableView.setMinWidth(1000);
 635 
 636         TreeTableRow row = (TreeTableRow) VirtualFlowTestUtils.getCell(tableView, 4);
 637         assertNotNull(row);
 638         assertNull(row.getItem());
 639         assertEquals(4, row.getIndex());
 640 
 641         MouseEventFirer mouse = new MouseEventFirer(row);
 642         mouse.fireMousePressAndRelease(1, 100, 10);
 643         mouse.dispose();
 644     }
 645 
 646     @Test public void test_rt_36509() {
 647         final int items = 8;
 648         root.getChildren().clear();
 649         root.setExpanded(false);
 650         for (int i = 0; i < items; i++) {
 651             root.getChildren().add(new TreeItem<>("Row " + i));
 652         }
 653         tableView.setRoot(root);
 654 
 655         // expand
 656         assertFalse(root.isExpanded());
 657         VirtualFlowTestUtils.clickOnRow(tableView, 0, 2);
 658         assertTrue(root.isExpanded());
 659 
 660         // collapse
 661         VirtualFlowTestUtils.clickOnRow(tableView, 0, 2);
 662         assertFalse(root.isExpanded());
 663 
 664         // expand again
 665         VirtualFlowTestUtils.clickOnRow(tableView, 0, 2);
 666         assertTrue(root.isExpanded());
 667     }
 668 
 669     @Test public void test_rt_37069() {
 670         final int items = 8;
 671         root.getChildren().clear();
 672         root.setExpanded(false);
 673         for (int i = 0; i < items; i++) {
 674             root.getChildren().add(new TreeItem<>("Row " + i));
 675         }
 676         tableView.setRoot(root);
 677         tableView.setFocusTraversable(false);
 678 
 679         Button btn = new Button("Button");
 680         VBox vbox = new VBox(btn, tableView);
 681 
 682         StageLoader sl = new StageLoader(vbox);
 683         sl.getStage().requestFocus();
 684         btn.requestFocus();
 685         Toolkit.getToolkit().firePulse();
 686         Scene scene = sl.getStage().getScene();
 687 
 688         assertTrue(btn.isFocused());
 689         assertFalse(tableView.isFocused());
 690 
 691         ScrollBar vbar = VirtualFlowTestUtils.getVirtualFlowVerticalScrollbar(tableView);
 692         MouseEventFirer mouse = new MouseEventFirer(vbar);
 693         mouse.fireMousePressAndRelease();
 694 
 695         assertTrue(btn.isFocused());
 696         assertFalse(tableView.isFocused());
 697 
 698         sl.dispose();
 699     }
 700 
 701     @Test public void test_rt_38306_selectFirstRow() {
 702         test_rt_38306(false);
 703     }
 704 
 705     @Test public void test_rt_38306_selectFirstTwoRows() {
 706         test_rt_38306(true);
 707     }
 708 
 709     private void test_rt_38306(boolean selectTwoRows) {
 710         TreeItem<Person> root = new TreeItem<>();
 711         root.getChildren().addAll(
 712                 new TreeItem<>(new Person("Jacob", "Smith", "jacob.smith@example.com")),
 713                 new TreeItem<>(new Person("Isabella", "Johnson", "isabella.johnson@example.com")),
 714                 new TreeItem<>(new Person("Ethan", "Williams", "ethan.williams@example.com")),
 715                 new TreeItem<>(new Person("Emma", "Jones", "emma.jones@example.com")),
 716                 new TreeItem<>(new Person("Michael", "Brown", "michael.brown@example.com"))
 717         );
 718 
 719         TreeTableView<Person> table = new TreeTableView<>();
 720         table.setRoot(root);
 721         root.setExpanded(true);
 722         table.setShowRoot(false);
 723 
 724         TreeTableView.TreeTableViewSelectionModel sm = table.getSelectionModel();
 725         sm.setCellSelectionEnabled(true);
 726         sm.setSelectionMode(SelectionMode.MULTIPLE);
 727 
 728         TreeTableColumn firstNameCol = new TreeTableColumn("First Name");
 729         firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("firstName"));
 730 
 731         TreeTableColumn lastNameCol = new TreeTableColumn("Last Name");
 732         lastNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("lastName"));
 733 
 734         TreeTableColumn emailCol = new TreeTableColumn("Email");
 735         emailCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("email"));
 736 
 737         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
 738 
 739         sm.select(0, firstNameCol);
 740 
 741         assertTrue(sm.isSelected(0, firstNameCol));
 742         assertEquals(1, sm.getSelectedCells().size());
 743 
 744         TreeTableCell cell_0_0 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 0, 0);
 745         TreeTableCell cell_0_1 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 0, 1);
 746         TreeTableCell cell_0_2 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 0, 2);
 747 
 748         TreeTableCell cell_1_0 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 1, 0);
 749         TreeTableCell cell_1_1 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 1, 1);
 750         TreeTableCell cell_1_2 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 1, 2);
 751 
 752         MouseEventFirer mouse = selectTwoRows ?
 753                 new MouseEventFirer(cell_1_2) : new MouseEventFirer(cell_0_2);
 754 
 755         mouse.fireMousePressAndRelease(KeyModifier.SHIFT);
 756 
 757         assertTrue(sm.isSelected(0, firstNameCol));
 758         assertTrue(sm.isSelected(0, lastNameCol));
 759         assertTrue(sm.isSelected(0, emailCol));
 760 
 761         if (selectTwoRows) {
 762             assertTrue(sm.isSelected(1, firstNameCol));
 763             assertTrue(sm.isSelected(1, lastNameCol));
 764             assertTrue(sm.isSelected(1, emailCol));
 765         }
 766 
 767         assertEquals(selectTwoRows ? 6 : 3, sm.getSelectedCells().size());
 768 
 769         assertTrue(cell_0_0.isSelected());
 770         assertTrue(cell_0_1.isSelected());
 771         assertTrue(cell_0_2.isSelected());
 772 
 773         if (selectTwoRows) {
 774             assertTrue(cell_1_0.isSelected());
 775             assertTrue(cell_1_1.isSelected());
 776             assertTrue(cell_1_2.isSelected());
 777         }
 778     }
 779 
 780     @Test public void test_rt_38464_rowSelection() {
 781         ObservableList<TreeItem<Person>> persons = FXCollections.observableArrayList(
 782                 new TreeItem<>(new Person("Jacob", "Smith", "jacob.smith@example.com")),
 783                 new TreeItem<>(new Person("Isabella", "Johnson", "isabella.johnson@example.com")),
 784                 new TreeItem<>(new Person("Ethan", "Williams", "ethan.williams@example.com")),
 785                 new TreeItem<>(new Person("Emma", "Jones", "emma.jones@example.com")),
 786                 new TreeItem<>(new Person("Michael", "Brown", "michael.brown@example.com")));
 787 
 788         TreeTableView<Person> table = new TreeTableView<>();
 789 
 790         TreeItem<Person> root = new TreeItem<Person>(new Person("Root", null, null));
 791         root.setExpanded(true);
 792         table.setRoot(root);
 793         table.setShowRoot(false);
 794         root.getChildren().setAll(persons);
 795 
 796         TreeTableColumn firstNameCol = new TreeTableColumn("First Name");
 797         firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("firstName"));
 798 
 799         TreeTableColumn lastNameCol = new TreeTableColumn("Last Name");
 800         lastNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("lastName"));
 801 
 802         TreeTableColumn emailCol = new TreeTableColumn("Email");
 803         emailCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("email"));
 804 
 805         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
 806 
 807         sm = table.getSelectionModel();
 808         sm.setCellSelectionEnabled(false);
 809         sm.setSelectionMode(SelectionMode.MULTIPLE);
 810 
 811         sm.clearSelection();
 812         sm.select(0, lastNameCol);
 813 
 814         assertTrue(sm.isSelected(0, lastNameCol));
 815         assertEquals(1, sm.getSelectedCells().size());
 816 
 817         TreeTableCell cell_4_2 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 4, 1);
 818 
 819         MouseEventFirer mouse = new MouseEventFirer(cell_4_2);
 820         mouse.fireMousePressAndRelease(KeyModifier.SHIFT);
 821 
 822         // we are in row selection mode, so all cells in the selected rows should
 823         // be selected. We test this per-cell, but also per-row.
 824         for (int row = 0; row < 5; row++) {
 825             // test that the selection model is accurate
 826             assertTrue(sm.isSelected(row, firstNameCol));
 827             assertTrue(sm.isSelected(row, lastNameCol));
 828             assertTrue(sm.isSelected(row, emailCol));
 829             assertTrue(sm.isSelected(row));
 830 
 831             // and assert that the visuals are accurate
 832             // (TableCells should not be selected, but TableRows should be)
 833             for (int column = 0; column < 3; column++) {
 834                 if (row == 4 && column == 2) {
 835                     // bizarrely cell (4,2), i.e. the bottom-right cell consisting
 836                     // of Michael Brown's email address, doesn't exist.
 837                     continue;
 838                 }
 839                 TreeTableCell cell = (TreeTableCell) VirtualFlowTestUtils.getCell(table, row, column);
 840                 assertFalse("cell[row: " + row + ", column: " + column + "] is selected, but shouldn't be", cell.isSelected());
 841             }
 842             TreeTableRow cell = (TreeTableRow) VirtualFlowTestUtils.getCell(table, row);
 843             assertTrue(cell.isSelected());
 844         }
 845     }
 846 
 847     @Test public void test_rt_38464_cellSelection() {
 848         ObservableList<TreeItem<Person>> persons = FXCollections.observableArrayList(
 849                 new TreeItem<>(new Person("Jacob", "Smith", "jacob.smith@example.com")),
 850                 new TreeItem<>(new Person("Isabella", "Johnson", "isabella.johnson@example.com")),
 851                 new TreeItem<>(new Person("Ethan", "Williams", "ethan.williams@example.com")),
 852                 new TreeItem<>(new Person("Emma", "Jones", "emma.jones@example.com")),
 853                 new TreeItem<>(new Person("Michael", "Brown", "michael.brown@example.com")));
 854 
 855         TreeTableView<Person> table = new TreeTableView<>();
 856 
 857         TreeItem<Person> root = new TreeItem<>(new Person("Root", null, null));
 858         root.setExpanded(true);
 859         table.setRoot(root);
 860         table.setShowRoot(false);
 861         root.getChildren().setAll(persons);
 862 
 863         TreeTableColumn firstNameCol = new TreeTableColumn("First Name");
 864         firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("firstName"));
 865 
 866         TreeTableColumn lastNameCol = new TreeTableColumn("Last Name");
 867         lastNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("lastName"));
 868 
 869         TreeTableColumn emailCol = new TreeTableColumn("Email");
 870         emailCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("email"));
 871 
 872         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
 873 
 874         sm = table.getSelectionModel();
 875         sm.setCellSelectionEnabled(true);
 876         sm.setSelectionMode(SelectionMode.MULTIPLE);
 877 
 878         sm.clearSelection();
 879         sm.select(0, emailCol);
 880         table.getFocusModel().focus(0, emailCol);
 881 
 882         assertTrue(sm.isSelected(0, emailCol));
 883         assertEquals(1, sm.getSelectedCells().size());
 884 
 885         TreeTableCell cell_4_2 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 4, 2);
 886         assertEquals(emailCol, cell_4_2.getTableColumn());
 887 
 888         new MouseEventFirer(cell_4_2).fireMousePressAndRelease(KeyModifier.SHIFT);
 889 
 890         for (int row = 0; row < 5; row++) {
 891             // test that the selection model is accurate
 892             assertFalse(sm.isSelected(row, firstNameCol));
 893             assertFalse(sm.isSelected(row, lastNameCol));
 894             assertTrue(sm.isSelected(row, emailCol));
 895             assertFalse(sm.isSelected(row));
 896 
 897             // and assert that the visuals are accurate
 898             // (some TableCells should be selected, but TableRows should not be)
 899             for (int column = 0; column < 3; column++) {
 900                 if (row == 4 && column == 2) {
 901                     // bizarrely cell (4,2), i.e. the bottom-right cell consisting
 902                     // of Michael Brown's email address, doesn't exist.
 903                     continue;
 904                 }
 905                 TreeTableCell cell = (TreeTableCell) VirtualFlowTestUtils.getCell(table, row, column);
 906                 assertEquals(column == 2 ? true : false, cell.isSelected());
 907             }
 908             TreeTableRow cell = (TreeTableRow) VirtualFlowTestUtils.getCell(table, row);
 909             assertFalse(cell.isSelected());
 910         }
 911     }
 912 
 913     @Test public void test_rt_38464_selectedColumnChangesWhenCellsInRowClicked_cellSelection_singleSelection() {
 914         test_rt_38464_selectedColumnChangesWhenCellsInRowClicked(true, true);
 915     }
 916 
 917     @Test public void test_rt_38464_selectedColumnChangesWhenCellsInRowClicked_cellSelection_multipleSelection() {
 918         test_rt_38464_selectedColumnChangesWhenCellsInRowClicked(true, false);
 919     }
 920 
 921     @Test public void test_rt_38464_selectedColumnChangesWhenCellsInRowClicked_rowSelection_singleSelection() {
 922         test_rt_38464_selectedColumnChangesWhenCellsInRowClicked(false, true);
 923     }
 924 
 925     @Test public void test_rt_38464_selectedColumnChangesWhenCellsInRowClicked_rowSelection_multipleSelection() {
 926         test_rt_38464_selectedColumnChangesWhenCellsInRowClicked(false, false);
 927     }
 928 
 929     private void test_rt_38464_selectedColumnChangesWhenCellsInRowClicked(boolean cellSelection, boolean singleSelection) {
 930         ObservableList<TreeItem<Person>> persons = FXCollections.observableArrayList(
 931                 new TreeItem<>(new Person("Jacob", "Smith", "jacob.smith@example.com")),
 932                 new TreeItem<>(new Person("Isabella", "Johnson", "isabella.johnson@example.com")),
 933                 new TreeItem<>(new Person("Ethan", "Williams", "ethan.williams@example.com")),
 934                 new TreeItem<>(new Person("Emma", "Jones", "emma.jones@example.com")),
 935                 new TreeItem<>(new Person("Michael", "Brown", "michael.brown@example.com")));
 936 
 937         TreeTableView<Person> table = new TreeTableView<>();
 938 
 939         TreeItem<Person> root = new TreeItem<Person>(new Person("Root", null, null));
 940         root.setExpanded(true);
 941         table.setRoot(root);
 942         table.setShowRoot(false);
 943         root.getChildren().setAll(persons);
 944 
 945         TreeTableColumn firstNameCol = new TreeTableColumn("First Name");
 946         firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("firstName"));
 947 
 948         TreeTableColumn lastNameCol = new TreeTableColumn("Last Name");
 949         lastNameCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("lastName"));
 950 
 951         TreeTableColumn emailCol = new TreeTableColumn("Email");
 952         emailCol.setCellValueFactory(new TreeItemPropertyValueFactory<Person, String>("email"));
 953 
 954         table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
 955 
 956         TreeTableView.TreeTableViewSelectionModel<Person> sm = table.getSelectionModel();
 957         sm.setCellSelectionEnabled(cellSelection);
 958         sm.setSelectionMode(singleSelection ? SelectionMode.SINGLE : SelectionMode.MULTIPLE);
 959 
 960         TreeTableCell cell_0_0 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 0, 0);
 961         TreeTableCell cell_0_1 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 0, 1);
 962         TreeTableCell cell_0_2 = (TreeTableCell) VirtualFlowTestUtils.getCell(table, 0, 2);
 963 
 964         sm.clearSelection();
 965 
 966         // click on cell (0,0).
 967         new MouseEventFirer(cell_0_0).fireMousePressAndRelease();
 968 
 969         if (cellSelection) {
 970             // Because we are in cell selection mode, this has the effect of
 971             // selecting just the one cell.
 972             assertFalse(sm.isSelected(0));
 973             assertTrue(sm.isSelected(0, firstNameCol));
 974             assertFalse(sm.isSelected(0, lastNameCol));
 975             assertFalse(sm.isSelected(0, emailCol));
 976             assertEquals(1, sm.getSelectedCells().size());
 977             assertEquals(0, sm.getSelectedCells().get(0).getRow());
 978             assertEquals(firstNameCol, sm.getSelectedCells().get(0).getTableColumn());
 979         } else {
 980             // Because we are in row selection mode, this has
 981             // the effect of selecting all cells and the backing row. However, the
 982             // selected cell will be (0, firstNameCol) only
 983             assertTrue(sm.isSelected(0));
 984             assertTrue(sm.isSelected(0, firstNameCol));
 985             assertTrue(sm.isSelected(0, lastNameCol));
 986             assertTrue(sm.isSelected(0, emailCol));
 987             assertEquals(1, sm.getSelectedCells().size());
 988             assertEquals(0, sm.getSelectedCells().get(0).getRow());
 989             assertEquals(firstNameCol, sm.getSelectedCells().get(0).getTableColumn());
 990         }
 991 
 992         // click on cell (0,1).
 993         new MouseEventFirer(cell_0_1).fireMousePressAndRelease();
 994 
 995         if (cellSelection) {
 996             // Everything should remain the same, except the
 997             // column of the single selected cell should change to lastNameCol.
 998             assertFalse(sm.isSelected(0));
 999             assertFalse(sm.isSelected(0, firstNameCol));
1000             assertTrue(sm.isSelected(0, lastNameCol));
1001             assertFalse(sm.isSelected(0, emailCol));
1002             assertEquals(1, sm.getSelectedCells().size());
1003             TreeTablePosition<?,?> cell = sm.getSelectedCells().get(0);
1004             assertEquals(0, cell.getRow());
1005             assertEquals(lastNameCol, cell.getTableColumn());
1006         } else {
1007             // Everything should remain the same, except the
1008             // column of the single selected cell should change to lastNameCol.
1009             assertTrue(sm.isSelected(0));
1010             assertTrue(sm.isSelected(0, firstNameCol));
1011             assertTrue(sm.isSelected(0, lastNameCol));
1012             assertTrue(sm.isSelected(0, emailCol));
1013             assertEquals(1, sm.getSelectedCells().size());
1014             TreeTablePosition<?,?> cell = sm.getSelectedCells().get(0);
1015             assertEquals(0, cell.getRow());
1016             assertEquals(lastNameCol, cell.getTableColumn());
1017         }
1018     }
1019 }