changeset 232:2351e0099f49

Partial progress on RT-18851: UX test cases for keyboard navigation in ListView (Essentially creating a bunch of unit tests to ensure proper keyboard navigation support in ListView)
author jgiles
date Tue, 10 Jan 2012 21:16:06 +1300
parents c411c4c0ba62
children 3a90e72e4f4d
files javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListCellBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/ListViewAnchorRetriever.java javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/TableViewAnchorRetriever.java javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/TreeViewAnchorRetriever.java javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java
diffstat 8 files changed, 1083 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListCellBehavior.java	Mon Jan 09 16:41:13 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListCellBehavior.java	Tue Jan 10 21:16:06 2012 +1300
@@ -48,7 +48,7 @@
         FocusModel fm = list.getFocusModel();
         if (fm == null) return -1;
         
-        return map.containsKey(list) ? map.get(list) : fm.getFocusedIndex();
+        return hasAnchor(list) ? map.get(list) : fm.getFocusedIndex();
     }
     
     static void setAnchor(ListView list, int anchor) {
@@ -59,6 +59,10 @@
         }
     }
     
+    static boolean hasAnchor(ListView list) {
+        return map.containsKey(list);
+    }
+    
     // For RT-17456: have selection occur as fast as possible with mouse input.
     // The idea is (consistently with some native applications we've tested) to 
     // do the action as soon as you can. It takes a bit more coding but provides
@@ -129,7 +133,7 @@
         // about).
         if (e.isShiftDown()) {
             if (! map.containsKey(listView)) {
-                map.put(listView, fm.getFocusedIndex());
+                setAnchor(listView, fm.getFocusedIndex());
             }
         } else {
             map.remove(listView);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java	Mon Jan 09 16:41:13 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java	Tue Jan 10 21:16:06 2012 +1300
@@ -101,7 +101,7 @@
             LIST_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").meta());
             LIST_VIEW_BINDINGS.add(new KeyBinding(HOME, "FocusFirstRow").meta());
             LIST_VIEW_BINDINGS.add(new KeyBinding(END, "FocusLastRow").meta());
-            LIST_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl().meta());
+            LIST_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").meta());
             LIST_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").meta());
             LIST_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").meta());
         } else {
@@ -247,8 +247,22 @@
         @Override public void onChanged(ListChangeListener.Change c) {
             while (c.next()) {
                 // there are no selected items, so lets clear out the anchor
-                if (! selectionChanging && c.getList().isEmpty()) {
-                    setAnchor(-1);
+                if (! selectionChanging) {
+                    if (c.getList().isEmpty()) {
+                        setAnchor(-1);
+                    } else if (! c.getList().contains(getAnchor())) {
+                        setAnchor(-1);
+                    }
+                }
+                
+                if (! hasAnchor() && c.getAddedSize() > 0) {
+                    for (int i = 0; i < c.getAddedSize(); i++) {
+                        int index = ((List<Integer>)c.getAddedSubList()).get(i);
+                        if (index >= 0) {
+                            setAnchor(index);
+                            break;
+                        }
+                    }
                 }
             }
         }
@@ -295,6 +309,10 @@
     private int getAnchor() {
         return ListCellBehavior.getAnchor(getControl());
     }
+    
+    private boolean hasAnchor() {
+        return ListCellBehavior.hasAnchor(getControl());
+    }
 
     @Override public void mousePressed(MouseEvent e) {
         super.mousePressed(e);
@@ -410,6 +428,11 @@
         if (isShiftDown && getAnchor() != -1) {
             int newRow = fm.getFocusedIndex() - 1;
             int anchor = getAnchor();
+            
+            if (! hasAnchor()) {
+                setAnchor(fm.getFocusedIndex());
+            } 
+            
             clearSelectionOutsideRange(anchor, newRow);
 
             if (anchor > newRow) {
@@ -434,6 +457,11 @@
         if (isShiftDown && getAnchor() != -1) {
             int newRow = fm.getFocusedIndex() + 1;
             int anchor = getAnchor();
+            
+            if (! hasAnchor()) {
+                setAnchor(fm.getFocusedIndex());
+            } 
+
             clearSelectionOutsideRange(anchor, newRow);
 
             if (anchor > newRow) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/ListViewAnchorRetriever.java	Tue Jan 10 21:16:06 2012 +1300
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+package com.sun.javafx.scene.control.behavior;
+
+import javafx.scene.control.ListView;
+
+public class ListViewAnchorRetriever {
+
+    // can only access the getAnchor method in ListCellBehavior from this package
+    public static int getAnchor(ListView listView) {
+        return ListCellBehavior.getAnchor(listView);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/TableViewAnchorRetriever.java	Tue Jan 10 21:16:06 2012 +1300
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+package com.sun.javafx.scene.control.behavior;
+
+import javafx.scene.control.TablePosition;
+import javafx.scene.control.TableView;
+
+public class TableViewAnchorRetriever {
+
+    // can only access the getAnchor method in TableCellBehavior from this package
+    public static TablePosition getAnchor(TableView tableView) {
+        return TableCellBehavior.getAnchor(tableView);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/TreeViewAnchorRetriever.java	Tue Jan 10 21:16:06 2012 +1300
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+package com.sun.javafx.scene.control.behavior;
+
+import javafx.scene.control.TreeView;
+
+public class TreeViewAnchorRetriever {
+
+    // can only access the getAnchor method in TreeCellBehavior from this package
+    public static int getAnchor(TreeView treeView) {
+        return TreeCellBehavior.getAnchor(treeView);
+    }
+}
--- a/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Mon Jan 09 16:41:13 2012 +1300
+++ b/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Tue Jan 10 21:16:06 2012 +1300
@@ -3,23 +3,19 @@
  */
 package javafx.scene.control;
 
+import com.sun.javafx.scene.control.behavior.ListViewAnchorRetriever;
 import static org.junit.Assert.*;
 
-import javafx.scene.control.KeyEventFirer;
-import javafx.scene.control.KeyModifier;
-
-import java.util.Arrays;
 import java.util.List;
-import javafx.event.EventType;
 import javafx.scene.Group;
 import javafx.scene.Scene;
 
 import javafx.scene.input.KeyCode;
-import javafx.scene.input.KeyEvent;
 import javafx.stage.Stage;
 import org.junit.After;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class ListViewKeyInputTest {
@@ -59,6 +55,53 @@
         stage.hide();
     }
     
+    
+    /***************************************************************************
+     * Util methods
+     **************************************************************************/
+    
+    private String debug() {
+        StringBuilder sb = new StringBuilder("Selected Indices: ");
+        
+        List<Integer> indices = sm.getSelectedIndices();
+        for (Integer index : indices) {
+            sb.append(index);
+            sb.append(", ");
+        }
+        
+        sb.append("\nAnchor: " + getAnchor());
+        return sb.toString();
+    }
+    
+    // Returns true if ALL indices are selected
+    private boolean isSelected(int... indices) {
+        for (int index : indices) {
+            if (! sm.isSelected(index)) return false;
+        }
+        return true;
+    }
+    
+    // Returns true if ALL indices are NOT selected
+    private boolean isNotSelected(int... indices) {
+        for (int index : indices) {
+            if (sm.isSelected(index)) return false;
+        }
+        return true;
+    }
+    
+    private int getAnchor() {
+        return ListViewAnchorRetriever.getAnchor(listView);
+    }
+    
+    private boolean isAnchor(int index) {
+        return getAnchor() == index;
+    }
+    
+    
+    /***************************************************************************
+     * General tests
+     **************************************************************************/    
+    
     @Test public void testInitialState() {
         assertTrue(sm.isSelected(0));
         assertEquals(1, sm.getSelectedIndices().size());
@@ -76,6 +119,20 @@
         assertTrue(sm.isSelected(1));
     }
     
+    @Test public void testDownArrowDoesNotChangeSelectionWhenAtLastIndex() {
+        int endIndex = listView.getItems().size() - 1;
+        sm.clearAndSelect(endIndex);
+        assertTrue(sm.isSelected(endIndex));
+        keyboard.doDownArrowPress();
+        assertTrue(sm.isSelected(endIndex));
+    }
+    
+    @Test public void testUpArrowDoesNotChangeSelectionWhenAt0Index() {
+        sm.clearAndSelect(0);
+        keyboard.doUpArrowPress();
+        testInitialState();
+    }
+    
     @Test public void testUpArrowChangesSelection() {
         sm.clearAndSelect(1);
         keyboard.doUpArrowPress();
@@ -93,6 +150,132 @@
         testInitialState();
     }
     
+    // test 19
+    @Test public void testCtrlDownMovesFocusButLeavesSelectionAlone() {
+        assertTrue(fm.isFocused(0));
+        keyboard.doDownArrowPress(KeyModifier.CTRL);
+        assertTrue(fm.isFocused(1));
+        assertTrue(sm.isSelected(0));
+        assertFalse(sm.isSelected(1));
+    }
+    
+    // test 20
+    @Test public void testCtrlUpDoesNotMoveFocus() {
+        assertTrue(fm.isFocused(0));
+        keyboard.doUpArrowPress(KeyModifier.CTRL);
+        assertTrue(fm.isFocused(0));
+        assertTrue(sm.isSelected(0));
+    }
+    
+    // test 21
+    @Test public void testCtrlLeftDoesNotMoveFocus() {
+        assertTrue(fm.isFocused(0));
+        keyboard.doLeftArrowPress(KeyModifier.CTRL);
+        assertTrue(fm.isFocused(0));
+        assertTrue(sm.isSelected(0));
+    }
+    
+    // test 22
+    @Test public void testCtrlRightDoesNotMoveFocus() {
+        assertTrue(fm.isFocused(0));
+        keyboard.doRightArrowPress(KeyModifier.CTRL);
+        assertTrue(fm.isFocused(0));
+        assertTrue(sm.isSelected(0));
+    }
+    
+    // test 23
+    @Test public void testCtrlUpMovesFocus() {
+        sm.clearAndSelect(1);
+        assertTrue(fm.isFocused(1));
+        assertTrue(sm.isSelected(1));
+        keyboard.doUpArrowPress(KeyModifier.CTRL);
+        assertTrue(fm.isFocused(0));
+        assertTrue(sm.isSelected(1));
+    }
+    
+    // test 24
+    @Test public void testCtrlDownDoesNotMoveFocusWhenAtLastIndex() {
+        int endIndex = listView.getItems().size() - 1;
+        sm.clearAndSelect(endIndex);
+        assertTrue(fm.isFocused(endIndex));
+        assertTrue(sm.isSelected(endIndex));
+        keyboard.doDownArrowPress(KeyModifier.CTRL);
+        assertTrue(fm.isFocused(endIndex));
+        assertTrue(sm.isSelected(endIndex));
+    }
+    
+    // test 25
+    @Test public void testCtrlDownArrowWithSpaceChangesAnchor() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 2
+        assertTrue(isSelected(0, 2));
+        assertTrue(isNotSelected(1));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 26
+    @Test public void testCtrlUpArrowWithSpaceChangesAnchor() {
+        sm.clearAndSelect(2);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 0
+        assertTrue(isSelected(0, 2));
+        assertTrue(isNotSelected(1));
+        assertTrue(isAnchor(0));
+    }
+    
+    // test 44
+    @Test public void testHomeKey() {
+        sm.clearAndSelect(3);
+        keyboard.doKeyPress(KeyCode.HOME);
+        assertTrue(isSelected(0));
+        assertTrue(isNotSelected(1,2,3));
+    }
+    
+    // test 45
+    @Test public void testEndKey() {
+        sm.clearAndSelect(3);
+        keyboard.doKeyPress(KeyCode.END);
+        assertTrue(isSelected(listView.getItems().size() - 1));
+        assertTrue(isNotSelected(1,2,3));
+    }
+    
+    // test 53
+    @Test public void testCtrlHome() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.CTRL);
+        assertTrue(isSelected(5));
+        assertTrue(fm.isFocused(0));
+    }
+    
+    // test 54
+    @Test public void testCtrlEnd() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.CTRL);
+        assertTrue(isSelected(5));
+        assertTrue(fm.isFocused(listView.getItems().size() - 1));
+    }
+    
+    // test 68
+    @Ignore("Bug still exists")
+    @Test public void testCtrlSpaceToClearSelection() {
+        sm.clearAndSelect(5);
+        assertTrue(isSelected(5));
+        assertTrue(fm.isFocused(5));
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);
+        assertTrue(isNotSelected(5));
+        assertTrue(fm.isFocused(5));
+        assertTrue(isAnchor(5));
+    }
+    
+    
+    
+    /***************************************************************************
+     * Tests for row-based multiple selection
+     **************************************************************************/
+    
     @Test public void testShiftDownArrowIncreasesSelection() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.SHIFT);
@@ -100,6 +283,14 @@
         assertTrue(sm.isSelected(1));
     }
     
+    @Test public void testShiftDownArrowDoesNotChangeSelectionWhenAtLastIndex() {
+        int endIndex = listView.getItems().size() - 1;
+        sm.clearAndSelect(endIndex);
+        assertTrue(sm.isSelected(endIndex));
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(endIndex));
+    }
+    
     @Test public void testShiftUpArrowIncreasesSelection() {
         sm.clearAndSelect(1);
         keyboard.doUpArrowPress(KeyModifier.SHIFT);
@@ -107,7 +298,483 @@
         assertTrue(sm.isSelected(1));
     }
     
+    @Test public void testShiftUpArrowWhenAt0Index() {
+        sm.clearAndSelect(0);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+    }
+    
+    @Test public void testShiftLeftArrowWhenAt0Index() {
+        sm.clearAndSelect(0);
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertFalse(sm.isSelected(1));
+    }
+    
+    @Test public void testShiftRightArrowWhenAt0Index() {
+        sm.clearAndSelect(0);
+        keyboard.doRightArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertFalse(sm.isSelected(1));
+    }
+    
+    @Test public void testShiftDownTwiceThenShiftUp() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        assertTrue(debug(), sm.isSelected(0));
+        assertTrue(sm.isSelected(1));
+        assertFalse(sm.isSelected(2));
+    }
+    
+    @Test public void testShiftUpTwiceThenShiftDownFrom0Index() {
+        sm.clearAndSelect(0);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertTrue(sm.isSelected(1));
+        assertFalse(sm.isSelected(2));
+    }
+    
+    @Test public void testShiftLeftTwiceThenShiftRight() {
+        sm.clearAndSelect(0);
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT);
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT);
+        keyboard.doRightArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertFalse(sm.isSelected(1));
+        assertFalse(sm.isSelected(2));
+    }
+    
+    @Test public void testShiftRightTwiceThenShiftLeft() {
+        sm.clearAndSelect(0);
+        keyboard.doRightArrowPress(KeyModifier.SHIFT);
+        keyboard.doRightArrowPress(KeyModifier.SHIFT);
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertFalse(sm.isSelected(1));
+        assertFalse(sm.isSelected(2));
+    }
+    
+    @Test public void testShiftUpTwiceThenShiftDown() {
+        sm.clearAndSelect(2);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        assertFalse(sm.isSelected(0));
+        assertTrue(sm.isSelected(1));
+        assertTrue(sm.isSelected(2));
+        assertFalse(sm.isSelected(3));
+    }
+    
+    // test 18 from Jindra's testcases.rtf file
+    @Test public void testShiftDownTwiceThenShiftUpWhenAtLastIndex() {
+        int endIndex = listView.getItems().size() - 1;
+        sm.clearAndSelect(endIndex);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(endIndex));
+        assertTrue(sm.isSelected(endIndex - 1));
+        assertFalse(sm.isSelected(endIndex - 2));
+    }
+    
+    // test 27
+    @Test public void testCtrlDownArrowWithSpaceChangesAnchor_extended() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 2
+        
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // deselect 0
+        assertTrue(isSelected(2));
+        assertTrue(isNotSelected(0, 1));
+        assertTrue(isAnchor(0));
+    }
+    
+    // test 28
+    @Test public void testCtrlUpArrowWithSpaceChangesAnchor_extended() {
+        sm.clearAndSelect(2);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 0
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // deselect 2
+        assertTrue(isSelected(0));
+        assertTrue(isNotSelected(1, 2));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 29
+    @Test public void testCtrlDownArrowWithSpaceChangesAnchor_extended2() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 2
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 4
+        assertTrue(isSelected(0, 2, 4));
+        assertTrue(isNotSelected(1, 3, 5));
+        assertTrue(isAnchor(4));
+    }
+    
+    // test 30
+    @Test public void testCtrlUpArrowWithSpaceChangesAnchor_extended2() {
+        sm.clearAndSelect(4);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 2
+        
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 0
+        assertTrue(isSelected(0, 2, 4));
+        assertTrue(isNotSelected(1, 3));
+        assertTrue(isAnchor(0));
+    }
+    
+    // test 31
+    @Ignore("Open question with Jindra")
+    @Test public void testCtrlDownArrowThenShiftSpaceToSelectRange() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);  // select 0,1,2
+        assertTrue(isSelected(0, 1, 2));
+        assertTrue(isNotSelected(3));
+        assertTrue(isAnchor(0));
+    }
+    
+    // test 32
+    @Ignore("Open question with Jindra")
+    @Test public void testCtrlUpArrowThenShiftSpaceToSelectRange() {
+        sm.clearAndSelect(2);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);  // select 0,1,2
+        assertTrue(isSelected(0, 1, 2));
+        assertTrue(isNotSelected(3));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 33
+    @Test public void testCtrlDownArrowThenSpaceToChangeSelection() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 2, keeping 0 selected
+        assertTrue(isSelected(0, 2));
+        assertTrue(isNotSelected(1, 3));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);  // select 2,3,4
+        assertTrue(isSelected(2, 3, 4));
+        assertTrue(isNotSelected(0, 1));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 34
+    @Ignore("Bug that must be fixed")
+    @Test public void testCtrlUpArrowThenSpaceToChangeSelection() {
+        sm.clearAndSelect(4);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 2, keeping 4 selected
+        assertTrue(isSelected(2, 4));
+        assertTrue(isNotSelected(0, 1, 3));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);  // select 0,1,2
+        assertTrue(isSelected(0, 1, 2));
+        assertTrue(isNotSelected(3, 4));
+        assertTrue(debug(), isAnchor(2));
+    }
+    
+    // test 35
+    @Test public void testCtrlDownTwiceThenShiftDown() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);  // select 0,1,2,3
+        assertTrue(isSelected(0, 1, 2, 3));
+    }
+    
+    // test 36
+    @Test public void testCtrlUpTwiceThenShiftDown() {
+        sm.clearAndSelect(3);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);  // select 1,2,3
+        assertTrue(isSelected(1, 2, 3));
+        assertTrue(isNotSelected(0));
+    }
+    
+    // test 37
+    @Test public void testCtrlDownThriceThenShiftUp() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doKeyPress(KeyCode.UP, KeyModifier.SHIFT);  // select 0,1,2
+        assertTrue(isSelected(0, 1, 2));
+        assertTrue(isNotSelected(3, 4));
+    }
+    
+    // test 38
+    @Test public void testCtrlUpTwiceThenShiftUp() {
+        sm.clearAndSelect(3);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doKeyPress(KeyCode.UP, KeyModifier.SHIFT);  // select 0,1,2,3
+        assertTrue(isSelected(0, 1, 2, 3));
+        assertTrue(isNotSelected(4));
+    }
+    
+    // test 39
+    @Test public void testCtrlDownTwiceThenSpace_extended() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 0,2
+        assertTrue(isSelected(0, 2));
+        assertTrue(isNotSelected(1, 3));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);   // select 2,3,4,5
+        assertTrue(isSelected(2, 3, 4, 5));
+        assertTrue(isNotSelected(0, 1));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 40
+    @Test public void testCtrlUpTwiceThenSpace_extended() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 3,5
+        assertTrue(isSelected(3,5));
+        assertTrue(isNotSelected(0,1,2,4));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 0
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);   // select 1,2,3
+        assertTrue(isSelected(1,2,3));
+        assertTrue(isNotSelected(0,4,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // test 41
+    @Test public void testCtrlDownTwiceThenSpace_extended2() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 0,2
+        assertTrue(isSelected(0,2));
+        assertTrue(isNotSelected(1,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 5
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);     // select 2,3,4
+        assertTrue(isSelected(2,3,4));
+        assertTrue(isNotSelected(0,1,5));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 50
+    @Ignore("Test fails")
+    @Test public void testCtrlDownThenShiftHome() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 0,2
+        assertTrue(isSelected(0,2));
+        assertTrue(isNotSelected(1,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2));
+        assertTrue(isNotSelected(3,4));
+        assertTrue(debug(),isAnchor(2));
+    }
+    
+    // test 51
+    @Ignore("Test fails")
+    @Test public void testCtrlUpThenShiftEnd() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 3,5
+        assertTrue(isSelected(3,5));
+        assertTrue(isNotSelected(1,2,4));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.SHIFT);
+        assertTrue(isSelected(3,4,5,6,7,8,9));
+        assertTrue(isNotSelected(0,1,2));
+        assertTrue(debug(),isAnchor(2));
+    }
+    
+    // test 42
+    @Test public void testCtrlUpTwiceThenSpace_extended2() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 4
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 3
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);  // select 3,5
+        assertTrue(isSelected(3,5));
+        assertTrue(isNotSelected(0,1,2,4));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 2
+        keyboard.doUpArrowPress(KeyModifier.CTRL);    // move focus to 1
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);     // select 0,1,2,3
+        assertTrue(isSelected(0,1,2,3));
+        assertTrue(isNotSelected(4,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // test 46
+    @Test public void testHomeKey_withSelectedItems() {
+        sm.clearSelection();
+        sm.selectRange(4, 11);
+        keyboard.doKeyPress(KeyCode.HOME);
+        assertTrue(isSelected(0));
+        assertTrue(isNotSelected(1,2,3,4,5,6,7,8,9,10,11));
+    }
+    
+    // test 47
+    @Test public void testEndKey_withSelectedItems() {
+        sm.clearSelection();
+        sm.selectRange(4, 11);
+        keyboard.doKeyPress(KeyCode.END);
+        assertTrue(isSelected(listView.getItems().size() - 1));
+        assertTrue(isNotSelected(1,2,3,4,5,6,7,8));
+    }
+    
+    // test 48
+    @Ignore("Bug still exists")
+    @Test public void testShiftHome() {
+        sm.clearAndSelect(3);
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3));
+        assertTrue(isNotSelected(4,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // test 49
+    @Test public void testShiftEnd() {
+        sm.clearAndSelect(3);
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.SHIFT);
+        assertTrue(isSelected(3,4,5,6,7,8,9));
+        assertTrue(isNotSelected(0,1,2));
+        assertTrue(isAnchor(3));
+    }
+    
+    // test 52
+    @Ignore("Bug still exists")
+    @Test public void testShiftHomeThenShiftEnd() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5));
+        assertTrue(isAnchor(5));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.SHIFT);
+        assertTrue(isSelected(5,6,7,8,9));
+        assertTrue(isAnchor(5));
+    }
+    
+    // test 65
+    @Test public void testShiftPageUp() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL);
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.CTRL);
+        keyboard.doDownArrowPress(KeyModifier.CTRL);
+        keyboard.doKeyPress(KeyCode.PAGE_UP, KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2));
+        assertTrue(isAnchor(2));
+    }
+    
+    // test 67
+    @Test public void testCtrlAToSelectAll() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.A, KeyModifier.CTRL);
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,8,9));
+    }
+    
+    
     /***************************************************************************
-     * Tests for row-based multiple selection
+     * Tests for editing
      **************************************************************************/
+    
+    // test 43 (part 1)
+    @Test public void testF2EntersEditModeAndEscapeCancelsEdit_part1() {
+        listView.setEditable(true);
+        
+        sm.clearAndSelect(0);
+        assertEquals(-1, listView.getEditingIndex());
+        keyboard.doKeyPress(KeyCode.F2);
+        assertEquals(0, listView.getEditingIndex());
+        
+        keyboard.doKeyPress(KeyCode.ESCAPE);
+        assertEquals(-1, listView.getEditingIndex());
+    }
+    
+//    // test 43 (part 2)
+//    @Test public void testF2EntersEditModeAndEscapeCancelsEdit_part2() {
+//        listView.setEditable(true);
+//        
+//        sm.clearAndSelect(0);
+//        keyboard.doKeyPress(KeyCode.F2);
+//        
+//        
+//    }
+    
+    /***************************************************************************
+     * Tests for specific bug reports
+     **************************************************************************/
+    
+    @Test public void test_rt18642() {
+        sm.clearAndSelect(1);                          // select 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 2
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 3
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL); // set anchor, and also select, 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 4
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 5
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL); // set anchor, and also select, 5
+        
+        assertTrue(isSelected(1, 3, 5));
+        assertTrue(isNotSelected(0, 2, 4));
+        
+        // anchor is at 5, so shift+UP should select rows 4 and 5 only
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);   
+        assertTrue(isSelected(4, 5));
+        assertTrue(isNotSelected(0, 1, 2, 3));
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Mon Jan 09 16:41:13 2012 +1300
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Tue Jan 10 21:16:06 2012 +1300
@@ -3,11 +3,14 @@
  */
 package javafx.scene.control;
 
+import com.sun.javafx.scene.control.behavior.ListViewAnchorRetriever;
+import com.sun.javafx.scene.control.behavior.TableViewAnchorRetriever;
 import static org.junit.Assert.*;
 
 import java.util.List;
 import javafx.scene.Group;
 import javafx.scene.Scene;
+import javafx.scene.input.KeyCode;
 
 import javafx.stage.Stage;
 import org.junit.After;
@@ -19,7 +22,7 @@
 public class TableViewKeyInputTest {
     private TableView<String> tableView;
     private TableView.TableViewSelectionModel<String> sm;
-    private FocusModel<String> fm;
+    private TableView.TableViewFocusModel<String> fm;
     
     private KeyEventFirer keyboard;
     
@@ -79,6 +82,26 @@
         return sb.toString();
     }
     
+    // Returns true if ALL indices are selected
+    private boolean isSelected(int... indices) {
+        for (int index : indices) {
+            if (! sm.isSelected(index)) return false;
+        }
+        return true;
+    }
+    
+    // Returns true if ALL indices are NOT selected
+    private boolean isNotSelected(int... indices) {
+        for (int index : indices) {
+            if (sm.isSelected(index)) return false;
+        }
+        return true;
+    }
+    
+    private TablePosition getAnchor() {
+        return TableViewAnchorRetriever.getAnchor(tableView);
+    }
+    
     /***************************************************************************
      * General tests
      **************************************************************************/    
@@ -250,4 +273,111 @@
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
     }
+    
+    @Ignore("This bug still exists")
+    @Test public void test_rt18536_positive_horizontal() {
+        // Test shift selection when focus is elsewhere (so as to select a range)
+        sm.setCellSelectionEnabled(true);
+        sm.clearAndSelect(1, col1);
+        
+        // move focus by holding down ctrl button
+        keyboard.doRightArrowPress(KeyModifier.CTRL);   // move focus to (1, col2)
+        keyboard.doRightArrowPress(KeyModifier.CTRL);   // move focus to (1, col3)
+        keyboard.doRightArrowPress(KeyModifier.CTRL);   // move focus to (1, col4)
+        keyboard.doRightArrowPress(KeyModifier.CTRL);   // move focus to (1, col5)
+        assertTrue(fm.isFocused(1, col5));
+        
+        // press shift + space to select all cells between (1, col1) and (1, col5)
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(1, col5));
+        assertTrue(sm.isSelected(1, col4));
+        assertTrue(sm.isSelected(1, col3));
+        assertTrue(sm.isSelected(1, col2));
+        assertTrue(sm.isSelected(1, col1));
+    }
+    
+    @Ignore("This bug still exists")
+    @Test public void test_rt18536_negative_horizontal() {
+        // Test shift selection when focus is elsewhere (so as to select a range)
+        sm.setCellSelectionEnabled(true);
+        sm.clearAndSelect(1, col5);
+        
+        // move focus by holding down ctrl button
+        keyboard.doLeftArrowPress(KeyModifier.CTRL);   // move focus to (1, col4)
+        keyboard.doLeftArrowPress(KeyModifier.CTRL);   // move focus to (1, col3)
+        keyboard.doLeftArrowPress(KeyModifier.CTRL);   // move focus to (1, col2)
+        keyboard.doLeftArrowPress(KeyModifier.CTRL);   // move focus to (1, col1)
+        assertTrue(fm.isFocused(1, col1));
+        
+        // press shift + space to select all cells between (1, col1) and (1, col5)
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(1, col5));
+        assertTrue(sm.isSelected(1, col4));
+        assertTrue(sm.isSelected(1, col3));
+        assertTrue(sm.isSelected(1, col2));
+        assertTrue(sm.isSelected(1, col1));
+    }
+    
+    @Ignore("This bug still exists")
+    @Test public void test_rt18536_positive_vertical() {
+        // Test shift selection when focus is elsewhere (so as to select a range)
+        sm.setCellSelectionEnabled(true);
+        sm.clearAndSelect(1, col5);
+        
+        // move focus by holding down ctrl button
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // move focus to (2, col5)
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // move focus to (3, col5)
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // move focus to (4, col5)
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // move focus to (5, col5)
+        assertTrue(fm.isFocused(5, col5));
+        
+        // press shift + space to select all cells between (1, col5) and (5, col5)
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(1, col5));
+        assertTrue(sm.isSelected(2, col5));
+        assertTrue(sm.isSelected(3, col5));
+        assertTrue(sm.isSelected(4, col5));
+        assertTrue(sm.isSelected(5, col5));
+    }
+    
+    @Ignore("This bug still exists")
+    @Test public void test_rt18536_negative_vertical() {
+        // Test shift selection when focus is elsewhere (so as to select a range)
+        sm.setCellSelectionEnabled(true);
+        sm.clearAndSelect(5, col5);
+        
+        // move focus by holding down ctrl button
+        keyboard.doUpArrowPress(KeyModifier.CTRL);   // move focus to (4, col5)
+        keyboard.doUpArrowPress(KeyModifier.CTRL);   // move focus to (3, col5)
+        keyboard.doUpArrowPress(KeyModifier.CTRL);   // move focus to (2, col5)
+        keyboard.doUpArrowPress(KeyModifier.CTRL);   // move focus to (1, col5)
+        assertTrue(fm.isFocused(1, col5));
+        
+        // press shift + space to select all cells between (1, col5) and (5, col5)
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(1, col5));
+        assertTrue(sm.isSelected(2, col5));
+        assertTrue(sm.isSelected(3, col5));
+        assertTrue(sm.isSelected(4, col5));
+        assertTrue(sm.isSelected(5, col5));
+    }
+    
+    @Test public void test_rt18642() {
+        sm.setCellSelectionEnabled(false);
+        sm.clearAndSelect(1);                          // select 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 2
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 3
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL); // set anchor, and also select, 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 4
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 5
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL); // set anchor, and also select, 5
+        
+        assertTrue(isSelected(1, 3, 5));
+        assertTrue(isNotSelected(0, 2, 4));
+        
+        // anchor is at 5, so shift+UP should select rows 4 and 5 only
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);   
+        assertTrue(isSelected(4, 5));
+        assertTrue(isNotSelected(0, 1, 2, 3));
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Tue Jan 10 21:16:06 2012 +1300
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+package javafx.scene.control;
+
+import com.sun.javafx.scene.control.behavior.ListViewAnchorRetriever;
+import com.sun.javafx.scene.control.behavior.TreeViewAnchorRetriever;
+import static org.junit.Assert.*;
+
+import javafx.scene.control.KeyEventFirer;
+import javafx.scene.control.KeyModifier;
+
+import java.util.Arrays;
+import java.util.List;
+import javafx.event.EventType;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.stage.Stage;
+import org.junit.After;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore("Not complete yet")
+public class TreeViewKeyInputTest {
+    private TreeView<String> treeView;
+    private MultipleSelectionModel<TreeItem<String>> sm;
+    private FocusModel<TreeItem<String>> fm;
+    
+    private KeyEventFirer keyboard;
+    
+    private Stage stage;
+    private Scene scene;
+    private Group group;
+    
+    private final TreeItem<String> root = new TreeItem<String>("Root");
+    private final TreeItem<String> child1 = new TreeItem<String>("Child 1");
+    private final TreeItem<String> child2 = new TreeItem<String>("Child 2");
+    private final TreeItem<String> child3 = new TreeItem<String>("Child 3");
+    private final TreeItem<String> child4 = new TreeItem<String>("Child 4");
+    private final TreeItem<String> child5 = new TreeItem<String>("Child 5");
+    private final TreeItem<String> child6 = new TreeItem<String>("Child 6");
+    private final TreeItem<String> child7 = new TreeItem<String>("Child 7");
+    private final TreeItem<String> child8 = new TreeItem<String>("Child 8");
+    private final TreeItem<String> child9 = new TreeItem<String>("Child 9");
+    private final TreeItem<String> child10 = new TreeItem<String>("Child 10");
+
+    public TreeViewKeyInputTest() {
+        root.getChildren().setAll(child1, child2, child3, child4, child5, child6, 
+                                    child7, child8, child9, child10 );
+    }
+    
+    @Before public void setup() {
+        treeView = new TreeView<String>();
+        sm = treeView.getSelectionModel();
+        fm = treeView.getFocusModel();
+        
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        
+        keyboard = new KeyEventFirer(treeView);
+        
+        group = new Group();
+        scene = new Scene(group);
+        
+        stage = new Stage();
+        stage.setScene(scene);
+        
+        group.getChildren().setAll(treeView);
+        stage.show();
+
+        treeView.setRoot(root);
+        sm.clearAndSelect(0);
+    }
+    
+    @After public void tearDown() {
+        stage.hide();
+    }
+    
+    
+    /***************************************************************************
+     * Util methods
+     **************************************************************************/
+    
+    private String debug() {
+        StringBuilder sb = new StringBuilder("Selected Indices: ");
+        
+        List<Integer> indices = sm.getSelectedIndices();
+        for (Integer index : indices) {
+            sb.append(index);
+            sb.append(", ");
+        }
+        return sb.toString();
+    }
+    
+    // Returns true if ALL indices are selected
+    private boolean isSelected(int... indices) {
+        for (int index : indices) {
+            if (! sm.isSelected(index)) return false;
+        }
+        return true;
+    }
+    
+    // Returns true if ALL indices are NOT selected
+    private boolean isNotSelected(int... indices) {
+        for (int index : indices) {
+            if (sm.isSelected(index)) return false;
+        }
+        return true;
+    }
+    
+    private int getAnchor() {
+        return TreeViewAnchorRetriever.getAnchor(treeView);
+    }
+    
+    
+    /***************************************************************************
+     * General tests
+     **************************************************************************/    
+    
+    @Test public void testInitialState() {
+        assertTrue(sm.isSelected(0));
+        assertEquals(1, sm.getSelectedIndices().size());
+        assertEquals(1, sm.getSelectedItems().size());
+    }
+    
+    /***************************************************************************
+     * Tests for row-based single selection
+     **************************************************************************/
+    
+    @Test public void testDownArrowChangesSelection() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress();
+        assertFalse(sm.isSelected(0));
+        assertTrue(sm.isSelected(1));
+    }
+    
+    @Test public void testUpArrowChangesSelection() {
+        sm.clearAndSelect(1);
+        keyboard.doUpArrowPress();
+        assertFalse(sm.isSelected(1));
+        assertTrue(sm.isSelected(0));
+    }
+    
+    @Test public void testLeftArrowDoesNotChangeState() {
+        keyboard.doLeftArrowPress();
+        testInitialState();
+    }
+    
+    @Test public void testRightArrowDoesNotChangeState() {
+        keyboard.doRightArrowPress();
+        testInitialState();
+    }
+    
+    @Test public void testShiftDownArrowIncreasesSelection() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertTrue(sm.isSelected(1));
+    }
+    
+    @Test public void testShiftUpArrowIncreasesSelection() {
+        sm.clearAndSelect(1);
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);
+        assertTrue(sm.isSelected(0));
+        assertTrue(sm.isSelected(1));
+    }
+    
+    /***************************************************************************
+     * Tests for row-based multiple selection
+     **************************************************************************/
+    
+    
+    /***************************************************************************
+     * Tests for specific bug reports
+     **************************************************************************/
+    
+    @Test public void test_rt18642() {
+        sm.clearAndSelect(1);                          // select 1
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 2
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 3
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL); // set anchor, and also select, 3
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 4
+        keyboard.doDownArrowPress(KeyModifier.CTRL);   // shift focus to 5
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.CTRL); // set anchor, and also select, 5
+        
+        assertTrue(isSelected(1, 3, 5));
+        assertTrue(isNotSelected(0, 2, 4));
+        
+        // anchor is at 5, so shift+UP should select rows 4 and 5 only
+        keyboard.doUpArrowPress(KeyModifier.SHIFT);   
+        assertTrue(isSelected(4, 5));
+        assertTrue(isNotSelected(0, 1, 2, 3));
+    }
+}