Fixing a bug in Cell Widgets when using the BOUND_TO_SELECTION KeyboardSelectionPolicy in which programmatically deselecting a value makes the value unselectable until the user selects a different value.  For example, in the list A, B, C, if you programmatically deselect value B, then value B cannot be selected unless the user selects value A or C first.  We now correctly detect this case and update the state with a null selected value, which allows us to detect the change when the user selects value B.

Review at http://gwt-code-reviews.appspot.com/1578805

Review by: skybrian@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10724 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java b/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
index 08875c1..a7d87f5 100644
--- a/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
+++ b/user/src/com/google/gwt/user/cellview/client/HasDataPresenter.java
@@ -1163,7 +1163,7 @@
          * or we will have a null selection event while we wait for asynchronous
          * data to load.
          */
-        if (newKey != null && !newKey.equals(oldKey)) {
+        if (newKey != null) {
           // Check both values for selection before setting selection, or the
           // selection model may resolve state early.
           boolean oldValueWasSelected =
@@ -1171,15 +1171,20 @@
           boolean newValueWasSelected =
               (newValue == null) ? false : selectionModel.isSelected(newValue);
 
-          // Deselect the old value.
-          if (oldValueWasSelected) {
-            selectionModel.setSelected(oldValue, false);
-          }
-
-          // Select the new value.
-          pending.selectedValue = newValue;
-          if (newValue != null && !newValueWasSelected) {
-            selectionModel.setSelected(newValue, true);
+          if (!newKey.equals(oldKey)) {
+            // Deselect the old value.
+            if (oldValueWasSelected) {
+              selectionModel.setSelected(oldValue, false);
+            }
+  
+            // Select the new value.
+            pending.selectedValue = newValue;
+            if (newValue != null && !newValueWasSelected) {
+              selectionModel.setSelected(newValue, true);
+            }
+          } else if (!newValueWasSelected) {
+            // The value was programmatically deselected.
+            pending.selectedValue = null;
           }
         }
       }
diff --git a/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java b/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
index e6c7fb1..95835c7 100644
--- a/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
+++ b/user/test/com/google/gwt/user/cellview/client/HasDataPresenterTest.java
@@ -630,6 +630,41 @@
   }
 
   /**
+   * Test that programmatically deselecting a row works.
+   */
+  public void testSetKeyboardSelectedRowBoundWithDeselect() {
+    HasData<String> listView = new MockHasData<String>();
+    MockView<String> view = new MockView<String>();
+    HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null);
+    presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION);
+    presenter.setVisibleRange(new Range(0, 10));
+    populatePresenter(presenter);
+    presenter.flush();
+
+    // Add a selection model.
+    MockSingleSelectionModel<String> model = new MockSingleSelectionModel<String>(null);
+    presenter.setSelectionModel(model);
+    presenter.flush();
+    assertEquals(null, model.getSelectedObject());
+
+    // Select an item.
+    presenter.setKeyboardSelectedRow(1, false, false);
+    presenter.flush();
+    assertTrue(model.isSelected("test 1"));
+
+    // Deselect the item.
+    model.setSelected("test 1", false);
+    assertFalse(model.isSelected("test 1"));
+    presenter.flush();
+    assertEquals(1, presenter.getKeyboardSelectedRow());
+
+    // Reselect the item.
+    presenter.setKeyboardSelectedRow(1, false, false);
+    presenter.flush();
+    assertTrue(model.isSelected("test 1"));
+  }
+
+  /**
    * Test that we only get one selection event when keyboard selection changes.
    */
   public void testSetKeyboardSelectedRowFiresOneSelectionEvent() {