changeset 4844:1d4565315819

RT-21444: SelectionModel.getSelectedItem() API does not work correctly in case when multiple selection is enabled and Shift key is down. Issue resolved in ListView, TreeView, TableView, and TreeTableView. Unit tests have been developed to prevent future regressions.
author jgiles
date Thu, 29 Aug 2013 15:43:42 +1200
parents bf88b13087fd
children 9b8894841264
files modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java modules/controls/src/test/java/javafx/scene/control/ListViewMouseInputTest.java modules/controls/src/test/java/javafx/scene/control/TableViewKeyInputTest.java modules/controls/src/test/java/javafx/scene/control/TableViewMouseInputTest.java modules/controls/src/test/java/javafx/scene/control/TreeTableViewMouseInputTest.java modules/controls/src/test/java/javafx/scene/control/TreeViewMouseInputTest.java
diffstat 10 files changed, 430 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListCellBehavior.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListCellBehavior.java	Thu Aug 29 15:43:42 2013 +1200
@@ -219,7 +219,8 @@
                 } else if (e.isShiftDown()) {
                     // we add all rows between the current focus and
                     // this row (inclusive) to the current selection.
-                    int focusIndex = getAnchor(listView);
+                    final int focusIndex = getAnchor(listView);
+                    final boolean asc = focusIndex < index;
 
                     // and then determine all row and columns which must be selected
                     int minRow = Math.min(focusIndex, index);
@@ -238,7 +239,16 @@
                             sm.clearSelection(selectedIndex);
                         }
                     }
-                    sm.selectRange(minRow, maxRow+1);
+
+                    // RT-21444: We need to put the range in in the correct
+                    // order or else the last selected row will not be the
+                    // last item in the selectedItems list of the selection
+                    // model,
+                    if (asc) {
+                        sm.selectRange(minRow, maxRow + 1);
+                    } else {
+                        sm.selectRange(maxRow, minRow - 1);
+                    }
 
                     // return selection back to the focus owner
                     fm.focus(index);
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java	Thu Aug 29 15:43:42 2013 +1200
@@ -240,7 +240,10 @@
                 } else if (e.isShiftDown()) {
                     // we add all cells/rows between the current selection focus and
                     // this cell/row (inclusive) to the current selection.
-                    TablePositionBase anchor = getAnchor(tableView, focusedCell);
+                    final TablePositionBase anchor = getAnchor(tableView, focusedCell);
+
+                    final int anchorRow = anchor.getRow();
+                    final boolean asc = anchorRow < row;
                     
                     // and then determine all row and columns which must be selected
                     int minRow = Math.min(anchor.getRow(), row);
@@ -269,8 +272,16 @@
                                 sm.clearSelection(selectedIndex);
                             }
                         }
-                        
-                        sm.selectRange(minRow, maxRow + 1);
+
+                        // RT-21444: We need to put the range in in the correct
+                        // order or else the last selected row will not be the
+                        // last item in the selectedItems list of the selection
+                        // model,
+                        if (asc) {
+                            sm.selectRange(minRow, maxRow + 1);
+                        } else {
+                            sm.selectRange(maxRow, minRow - 1);
+                        }
                     }
 
                     // This line of code below was disabled as a fix for RT-30394.
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehavior.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehavior.java	Thu Aug 29 15:43:42 2013 +1200
@@ -76,10 +76,12 @@
                     // we add all rows between the current focus and
                     // this row (inclusive) to the current selection.
                     TablePositionBase anchor = TableCellBehavior.getAnchor(table, table.getFocusModel().getFocusedCell());
+                    final int anchorRow = anchor.getRow();
+                    final boolean asc = anchorRow < index;
 
                     // and then determine all row and columns which must be selected
-                    int minRow = Math.min(anchor.getRow(), index);
-                    int maxRow = Math.max(anchor.getRow(), index);
+                    int minRow = Math.min(anchorRow, index);
+                    int maxRow = Math.max(anchorRow, index);
 
                     // To prevent RT-32119, we make a copy of the selected indices
                     // list first, so that we are not iterating and modifying it
@@ -92,7 +94,15 @@
                         }
                     }
 
-                    sm.selectRange(minRow, maxRow + 1);
+                    // RT-21444: We need to put the range in in the correct
+                    // order or else the last selected row will not be the
+                    // last item in the selectedItems list of the selection
+                    // model,
+                    if (asc) {
+                        sm.selectRange(minRow, maxRow + 1);
+                    } else {
+                        sm.selectRange(maxRow, minRow - 1);
+                    }
                 } else {
                     sm.clearAndSelect(tableRow.getIndex());
                 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java	Thu Aug 29 15:43:42 2013 +1200
@@ -232,6 +232,7 @@
                     // we add all rows between the current selection focus and
                     // this row (inclusive) to the current selection.
                     final int focusedIndex = getAnchor(treeView);
+                    final boolean asc = focusedIndex < index;
 
                     // and then determine all row and columns which must be selected
                     int minRow = Math.min(focusedIndex, index);
@@ -250,7 +251,16 @@
                             sm.clearSelection(selectedIndex);
                         }
                     }
-                    sm.selectRange(minRow, maxRow+1);
+
+                    // RT-21444: We need to put the range in in the correct
+                    // order or else the last selected row will not be the
+                    // last item in the selectedItems list of the selection
+                    // model,
+                    if (asc) {
+                        sm.selectRange(minRow, maxRow + 1);
+                    } else {
+                        sm.selectRange(maxRow, minRow - 1);
+                    }
 
                     fm.focus(index);
                 } else {
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java	Thu Aug 29 15:43:42 2013 +1200
@@ -91,6 +91,8 @@
                     // we add all rows between the current focus and
                     // this row (inclusive) to the current selection.
                     TablePositionBase anchor = TreeTableCellBehavior.getAnchor(table, table.getFocusModel().getFocusedCell());
+                    final int anchorRow = anchor.getRow();
+                    final boolean asc = anchorRow < index;
 
                     // and then determine all row and columns which must be selected
                     int minRow = Math.min(anchor.getRow(), index);
@@ -107,7 +109,15 @@
                         }
                     }
 
-                    sm.selectRange(minRow, maxRow + 1);
+                    // RT-21444: We need to put the range in in the correct
+                    // order or else the last selected row will not be the
+                    // last item in the selectedItems list of the selection
+                    // model,
+                    if (asc) {
+                        sm.selectRange(minRow, maxRow + 1);
+                    } else {
+                        sm.selectRange(maxRow, minRow - 1);
+                    }
                 } else {
                     sm.clearAndSelect(treeTableRow.getIndex());
                 }
--- a/modules/controls/src/test/java/javafx/scene/control/ListViewMouseInputTest.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/test/java/javafx/scene/control/ListViewMouseInputTest.java	Thu Aug 29 15:43:42 2013 +1200
@@ -199,4 +199,48 @@
         assertFalse(sm.isSelected(4));
         assertFalse(sm.isSelected(5));
     }
+
+    @Test public void test_rt21444_up() {
+        final int items = 8;
+        listView.getItems().clear();
+        for (int i = 1; i <= items; i++) {
+            listView.getItems().add("Row " + i);
+        }
+
+        final int selectRow = 3;
+
+        final MultipleSelectionModel sm = listView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 4", sm.getSelectedItem());
+
+        VirtualFlowTestUtils.clickOnRow(listView, selectRow - 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 3", sm.getSelectedItem());
+        assertEquals("Row 3", sm.getSelectedItems().get(0));
+    }
+
+    @Test public void test_rt21444_down() {
+        final int items = 8;
+        listView.getItems().clear();
+        for (int i = 1; i <= items; i++) {
+            listView.getItems().add("Row " + i);
+        }
+
+        final int selectRow = 3;
+
+        final MultipleSelectionModel sm = listView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 4", sm.getSelectedItem());
+
+        VirtualFlowTestUtils.clickOnRow(listView, selectRow + 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 5", sm.getSelectedItem());
+        assertEquals("Row 5", sm.getSelectedItems().get(1));
+    }
 }
--- a/modules/controls/src/test/java/javafx/scene/control/TableViewKeyInputTest.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/test/java/javafx/scene/control/TableViewKeyInputTest.java	Thu Aug 29 15:43:42 2013 +1200
@@ -2103,4 +2103,46 @@
         assertNotSame(initialSelectionOwner, nextSelectionOwner);
         assertNotSame(newSelectionOwner, nextSelectionOwner);
     }
+
+    @Test public void test_rt21444_up() {
+        final int items = 8;
+        tableView.getItems().clear();
+        for (int i = 1; i <= items; i++) {
+            tableView.getItems().add("Row " + i);
+        }
+
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(3);
+
+        assertEquals(3, sm.getSelectedIndex());
+        assertEquals("Row 4", sm.getSelectedItem());
+
+        keyboard.doKeyPress(KeyCode.UP, KeyModifier.SHIFT);
+        Toolkit.getToolkit().firePulse();
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 3", sm.getSelectedItem());
+        assertEquals("Row 3", sm.getSelectedItems().get(0));
+    }
+
+    @Test public void test_rt21444_down() {
+        final int items = 8;
+        tableView.getItems().clear();
+        for (int i = 1; i <= items; i++) {
+            tableView.getItems().add("Row " + i);
+        }
+
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(3);
+
+        assertEquals(3, sm.getSelectedIndex());
+        assertEquals("Row 4", sm.getSelectedItem());
+
+        keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);
+        Toolkit.getToolkit().firePulse();
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 5", sm.getSelectedItem());
+        assertEquals("Row 5", sm.getSelectedItems().get(1));
+    }
 }
--- a/modules/controls/src/test/java/javafx/scene/control/TableViewMouseInputTest.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/test/java/javafx/scene/control/TableViewMouseInputTest.java	Thu Aug 29 15:43:42 2013 +1200
@@ -244,4 +244,92 @@
         assertTrue(sm.isSelected(4));
         assertTrue(sm.isSelected(5));
     }
+
+    @Test public void test_rt21444_up_cell() {
+        final int items = 8;
+        tableView.getItems().clear();
+        for (int i = 0; i < items; i++) {
+            tableView.getItems().add("Row " + i);
+        }
+
+        final int selectRow = 3;
+
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow - 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 2", sm.getSelectedItem());
+        assertEquals("Row 2", sm.getSelectedItems().get(0));
+    }
+
+    @Test public void test_rt21444_down_cell() {
+        final int items = 8;
+        tableView.getItems().clear();
+        for (int i = 0; i < items; i++) {
+            tableView.getItems().add("Row " + i);
+        }
+
+        final int selectRow = 3;
+
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow + 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 4", sm.getSelectedItem());
+        assertEquals("Row 4", sm.getSelectedItems().get(1));
+    }
+
+    @Test public void test_rt21444_up_row() {
+        final int items = 8;
+        tableView.getItems().clear();
+        for (int i = 0; i < items; i++) {
+            tableView.getItems().add("Row " + i);
+        }
+
+        final int selectRow = 3;
+
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow - 1, true, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 2", sm.getSelectedItem());
+        assertEquals("Row 2", sm.getSelectedItems().get(0));
+    }
+
+    @Test public void test_rt21444_down_row() {
+        final int items = 8;
+        tableView.getItems().clear();
+        for (int i = 0; i < items; i++) {
+            tableView.getItems().add("Row " + i);
+        }
+
+        final int selectRow = 3;
+
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow + 1, true, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 4", sm.getSelectedItem());
+        assertEquals("Row 4", sm.getSelectedItems().get(1));
+    }
 }
--- a/modules/controls/src/test/java/javafx/scene/control/TreeTableViewMouseInputTest.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/test/java/javafx/scene/control/TreeTableViewMouseInputTest.java	Thu Aug 29 15:43:42 2013 +1200
@@ -62,8 +62,6 @@
     private TreeTableView.TreeTableViewSelectionModel<String> sm;
     private TreeTableView.TreeTableViewFocusModel<String> fm;
     
-    private KeyEventFirer keyboard;
-    
     private StageLoader stageLoader;
     
     private final TreeTableColumn<String, String> col0 = new TreeTableColumn<String, String>("col0");
@@ -71,23 +69,38 @@
     private final TreeTableColumn<String, String> col2 = new TreeTableColumn<String, String>("col2");
     private final TreeTableColumn<String, String> col3 = new TreeTableColumn<String, String>("col3");
     private final TreeTableColumn<String, String> col4 = new TreeTableColumn<String, String>("col4");
-    
-    private final TreeItem<String> root = new TreeItem<String>("Root");                     // 0
-        private final TreeItem<String> child1 = new TreeItem<String>("Child 1");            // 1
-        private final TreeItem<String> child2 = new TreeItem<String>("Child 2");            // 2
-        private final TreeItem<String> child3 = new TreeItem<String>("Child 3");            // 3
-            private final TreeItem<String> subchild1 = new TreeItem<String>("Subchild 1");  // 4
-            private final TreeItem<String> subchild2 = new TreeItem<String>("Subchild 2");  // 5
-            private final TreeItem<String> subchild3 = new TreeItem<String>("Subchild 3");  // 6
-        private final TreeItem<String> child4 = new TreeItem<String>("Child 4");            // 7
-        private final TreeItem<String> child5 = new TreeItem<String>("Child 5");            // 8
-        private final TreeItem<String> child6 = new TreeItem<String>("Child 6");            // 9
-        private final TreeItem<String> child7 = new TreeItem<String>("Child 7");            // 10
-        private final TreeItem<String> child8 = new TreeItem<String>("Child 8");            // 11
-        private final TreeItem<String> child9 = new TreeItem<String>("Child 9");            // 12
-        private final TreeItem<String> child10 = new TreeItem<String>("Child 10");          // 13
-    
+
+    private TreeItem<String> root;                  // 0
+    private TreeItem<String> child1;            // 1
+    private TreeItem<String> child2;            // 2
+    private TreeItem<String> child3;            // 3
+    private TreeItem<String> subchild1;     // 4
+    private TreeItem<String> subchild2;     // 5
+    private TreeItem<String> subchild3;     // 6
+    private TreeItem<String> child4;            // 7
+    private TreeItem<String> child5;            // 8
+    private TreeItem<String> child6;            // 9
+    private TreeItem<String> child7;            // 10
+    private TreeItem<String> child8;            // 11
+    private TreeItem<String> child9;            // 12
+    private TreeItem<String> child10;           // 13
+
     @Before public void setup() {
+        root = new TreeItem<String>("Root");             // 0
+        child1 = new TreeItem<String>("Child 1");        // 1
+        child2 = new TreeItem<String>("Child 2");        // 2
+        child3 = new TreeItem<String>("Child 3");        // 3
+        subchild1 = new TreeItem<String>("Subchild 1");  // 4
+        subchild2 = new TreeItem<String>("Subchild 2");  // 5
+        subchild3 = new TreeItem<String>("Subchild 3");  // 6
+        child4 = new TreeItem<String>("Child 4");        // 7
+        child5 = new TreeItem<String>("Child 5");        // 8
+        child6 = new TreeItem<String>("Child 6");        // 9
+        child7 = new TreeItem<String>("Child 7");        // 10
+        child8 = new TreeItem<String>("Child 8");        // 11
+        child9 = new TreeItem<String>("Child 9");        // 12
+        child10 = new TreeItem<String>("Child 10");      // 13
+
         // reset tree structure
         root.getChildren().clear();
         root.setExpanded(true);
@@ -126,8 +139,6 @@
         
         sm.clearAndSelect(0);
         
-        keyboard = new KeyEventFirer(tableView);
-        
         stageLoader = new StageLoader(tableView);
         stageLoader.getStage().show();
     }
@@ -135,6 +146,7 @@
     @After public void tearDown() {
         tableView.getSkin().dispose();
         stageLoader.dispose();
+        sm = null;
     }
     
     /***************************************************************************
@@ -302,4 +314,92 @@
         assertTrue(sm.isSelected(4));
         assertTrue(sm.isSelected(5));
     }
+
+    @Test public void test_rt21444_up_cell() {
+        final int items = 8;
+        root.getChildren().clear();
+        for (int i = 0; i < items; i++) {
+            root.getChildren().add(new TreeItem<>("Row " + i));
+        }
+
+        final int selectRow = 3;
+
+        tableView.setShowRoot(false);
+        final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem().getValue());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow - 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 2", sm.getSelectedItem().getValue());
+        assertEquals("Row 2", sm.getSelectedItems().get(1).getValue());
+    }
+
+    @Test public void test_rt21444_down_cell() {
+        final int items = 8;
+        root.getChildren().clear();
+        for (int i = 0; i < items; i++) {
+            root.getChildren().add(new TreeItem<>("Row " + i));
+        }
+
+        final int selectRow = 3;
+
+        tableView.setShowRoot(false);
+        final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem().getValue());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow + 1, KeyModifier.SHIFT);
+        assertEquals("Row 4", sm.getSelectedItem().getValue());
+    }
+
+    @Test public void test_rt21444_up_row() {
+        final int items = 8;
+        root.getChildren().clear();
+        for (int i = 0; i < items; i++) {
+            root.getChildren().add(new TreeItem<>("Row " + i));
+        }
+
+        final int selectRow = 3;
+
+        tableView.setShowRoot(false);
+        final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem().getValue());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow - 1, true, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 2", sm.getSelectedItem().getValue());
+        assertEquals("Row 2", sm.getSelectedItems().get(1).getValue());
+    }
+
+    @Test public void test_rt21444_down_row() {
+        final int items = 8;
+        root.getChildren().clear();
+        for (int i = 0; i < items; i++) {
+            root.getChildren().add(new TreeItem<>("Row " + i));
+        }
+
+        final int selectRow = 3;
+
+        tableView.setShowRoot(false);
+        final MultipleSelectionModel<TreeItem<String>> sm = tableView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem().getValue());
+
+        VirtualFlowTestUtils.clickOnRow(tableView, selectRow + 1, true, KeyModifier.SHIFT);
+        assertEquals("Row 4", sm.getSelectedItem().getValue());
+    }
 }
--- a/modules/controls/src/test/java/javafx/scene/control/TreeViewMouseInputTest.java	Thu Aug 29 09:34:24 2013 +1200
+++ b/modules/controls/src/test/java/javafx/scene/control/TreeViewMouseInputTest.java	Thu Aug 29 15:43:42 2013 +1200
@@ -60,22 +60,37 @@
     private KeyEventFirer keyboard;
     private StageLoader stageLoader;
     
-    private final TreeItem<String> root = new TreeItem<String>("Root");                     // 0
-        private final TreeItem<String> child1 = new TreeItem<String>("Child 1");            // 1
-        private final TreeItem<String> child2 = new TreeItem<String>("Child 2");            // 2
-        private final TreeItem<String> child3 = new TreeItem<String>("Child 3");            // 3
-            private final TreeItem<String> subchild1 = new TreeItem<String>("Subchild 1");  // 4
-            private final TreeItem<String> subchild2 = new TreeItem<String>("Subchild 2");  // 5
-            private final TreeItem<String> subchild3 = new TreeItem<String>("Subchild 3");  // 6
-        private final TreeItem<String> child4 = new TreeItem<String>("Child 4");            // 7
-        private final TreeItem<String> child5 = new TreeItem<String>("Child 5");            // 8
-        private final TreeItem<String> child6 = new TreeItem<String>("Child 6");            // 9
-        private final TreeItem<String> child7 = new TreeItem<String>("Child 7");            // 10
-        private final TreeItem<String> child8 = new TreeItem<String>("Child 8");            // 11
-        private final TreeItem<String> child9 = new TreeItem<String>("Child 9");            // 12
-        private final TreeItem<String> child10 = new TreeItem<String>("Child 10");          // 13
+    private TreeItem<String> root;                  // 0
+        private TreeItem<String> child1;            // 1
+        private TreeItem<String> child2;            // 2
+        private TreeItem<String> child3;            // 3
+            private TreeItem<String> subchild1;     // 4
+            private TreeItem<String> subchild2;     // 5
+            private TreeItem<String> subchild3;     // 6
+        private TreeItem<String> child4;            // 7
+        private TreeItem<String> child5;            // 8
+        private TreeItem<String> child6;            // 9
+        private TreeItem<String> child7;            // 10
+        private TreeItem<String> child8;            // 11
+        private TreeItem<String> child9;            // 12
+        private TreeItem<String> child10;           // 13
 
     @Before public void setup() {
+        root = new TreeItem<String>("Root");             // 0
+        child1 = new TreeItem<String>("Child 1");        // 1
+        child2 = new TreeItem<String>("Child 2");        // 2
+        child3 = new TreeItem<String>("Child 3");        // 3
+        subchild1 = new TreeItem<String>("Subchild 1");  // 4
+        subchild2 = new TreeItem<String>("Subchild 2");  // 5
+        subchild3 = new TreeItem<String>("Subchild 3");  // 6
+        child4 = new TreeItem<String>("Child 4");        // 7
+        child5 = new TreeItem<String>("Child 5");        // 8
+        child6 = new TreeItem<String>("Child 6");        // 9
+        child7 = new TreeItem<String>("Child 7");        // 10
+        child8 = new TreeItem<String>("Child 8");        // 11
+        child9 = new TreeItem<String>("Child 9");        // 12
+        child10 = new TreeItem<String>("Child 10");      // 13
+
         // reset tree structure
         root.getChildren().clear();
         root.setExpanded(true);
@@ -251,4 +266,50 @@
         assertFalse(sm.isSelected(4));
         assertFalse(sm.isSelected(5));
     }
+
+    @Test public void test_rt21444_up() {
+        final int items = 8;
+        root.getChildren().clear();
+        for (int i = 0; i < items; i++) {
+            root.getChildren().add(new TreeItem<>("Row " + i));
+        }
+
+        final int selectRow = 3;
+
+        treeView.setShowRoot(false);
+        final MultipleSelectionModel<TreeItem<String>> sm = treeView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem().getValue());
+
+        VirtualFlowTestUtils.clickOnRow(treeView, selectRow - 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 2", sm.getSelectedItem().getValue());
+        assertEquals("Row 2", sm.getSelectedItems().get(0).getValue());
+    }
+
+    @Test public void test_rt21444_down() {
+        final int items = 8;
+        root.getChildren().clear();
+        for (int i = 0; i < items; i++) {
+            root.getChildren().add(new TreeItem<>("Row " + i));
+        }
+
+        final int selectRow = 3;
+
+        treeView.setShowRoot(false);
+        final MultipleSelectionModel<TreeItem<String>> sm = treeView.getSelectionModel();
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.clearAndSelect(selectRow);
+
+        assertEquals(selectRow, sm.getSelectedIndex());
+        assertEquals("Row 3", sm.getSelectedItem().getValue());
+
+        VirtualFlowTestUtils.clickOnRow(treeView, selectRow + 1, KeyModifier.SHIFT);
+        assertEquals(2, sm.getSelectedItems().size());
+        assertEquals("Row 4", sm.getSelectedItem().getValue());
+        assertEquals("Row 4", sm.getSelectedItems().get(1).getValue());
+    }
 }