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