Adding a BlacklistEventTranslator and WhitelistEventTranslator to DefaultSelectionEventManager to make it easy to deny/allow selection for specific columns in a CellTable. This is useful when you have a Cell that responds to events, such as a button or link, and should not participate in Selection.

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

Review by: sbrubaker@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9788 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java b/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
index 6c94271..00de218 100644
--- a/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
+++ b/user/src/com/google/gwt/user/cellview/client/AbstractHasData.java
@@ -598,6 +598,24 @@
     presenter.setRowData(start, values);
   }
 
+  /**
+   * Set the {@link SelectionModel} used by this {@link HasData}.
+   * 
+   * <p>
+   * By default, selection occurs when the user clicks on a Cell or presses the
+   * spacebar. If you need finer control over selection, you can specify a
+   * {@link DefaultSelectionEventManager} using 
+   * {@link #setSelectionModel(SelectionModel, com.google.gwt.view.client.CellPreviewEvent.Handler)}.
+   * {@link DefaultSelectionEventManager} provides some default
+   * implementations to handle checkbox based selection, as well as a blacklist
+   * or whitelist of columns to prevent or allow selection.
+   * </p>
+   * 
+   * @param selectionModel the {@link SelectionModel}
+   * @see #setSelectionModel(SelectionModel,
+   *      com.google.gwt.view.client.CellPreviewEvent.Handler)
+   * @see #getSelectionModel()
+   */
   public void setSelectionModel(SelectionModel<? super T> selectionModel) {
     presenter.setSelectionModel(selectionModel);
   }
diff --git a/user/src/com/google/gwt/view/client/DefaultSelectionEventManager.java b/user/src/com/google/gwt/view/client/DefaultSelectionEventManager.java
index 22b40cc..5651fe4 100644
--- a/user/src/com/google/gwt/view/client/DefaultSelectionEventManager.java
+++ b/user/src/com/google/gwt/view/client/DefaultSelectionEventManager.java
@@ -20,7 +20,9 @@
 import com.google.gwt.dom.client.NativeEvent;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * An implementation of {@link CellPreviewEvent.Handler} that adds selection
@@ -39,6 +41,69 @@
     CellPreviewEvent.Handler<T> {
 
   /**
+   * An event translator that disables selection for the specified blacklisted
+   * columns.
+   * 
+   * @param <T> the data type
+   */
+  public static class BlacklistEventTranslator<T> implements EventTranslator<T> {
+    private final Set<Integer> blacklist = new HashSet<Integer>();
+
+    /**
+     * Construct a new {@link BlacklistEventTranslator}.
+     * 
+     * @param blacklistedColumns the columns to blacklist
+     */
+    public BlacklistEventTranslator(int... blacklistedColumns) {
+      if (blacklistedColumns != null) {
+        for (int i : blacklistedColumns) {
+          setColumnBlacklisted(i, true);
+        }
+      }
+    }
+
+    /**
+     * Clear all columns from the blacklist.
+     */
+    public void clearBlacklist() {
+      blacklist.clear();
+    }
+
+    public boolean clearCurrentSelection(CellPreviewEvent<T> event) {
+      return false;
+    }
+
+    /**
+     * Check if the specified column is blacklisted.
+     * 
+     * @param index the column index
+     * @return true if blacklisted, false if not
+     */
+    public boolean isColumnBlacklisted(int index) {
+      return blacklist.contains(index);
+    }
+
+    /**
+     * Set whether or not the specified column in blacklisted.
+     * 
+     * @param index the column index
+     * @param isBlacklisted true to blacklist, false to allow selection
+     */
+    public void setColumnBlacklisted(int index, boolean isBlacklisted) {
+      if (isBlacklisted) {
+        blacklist.add(index);
+      } else {
+        blacklist.remove(index);
+      }
+    }
+
+    public SelectAction translateSelectionEvent(CellPreviewEvent<T> event) {
+      return isColumnBlacklisted(event.getColumn()) ? SelectAction.IGNORE
+          : SelectAction.DEFAULT;
+    }
+  }
+  
+  /**
    * Implementation of {@link EventTranslator} that only triggers selection when
    * any checkbox is selected.
    * 
@@ -133,6 +198,83 @@
   }
 
   /**
+   * An event translator that allows selection only for the specified
+   * whitelisted columns.
+   * 
+   * @param <T> the data type
+   */
+  public static class WhitelistEventTranslator<T> implements EventTranslator<T> {
+    private final Set<Integer> whitelist = new HashSet<Integer>();
+
+    /**
+     * Construct a new {@link WhitelistEventTranslator}.
+     * 
+     * @param whitelistedColumns the columns to whitelist
+     */
+    public WhitelistEventTranslator(int... whitelistedColumns) {
+      if (whitelistedColumns != null) {
+        for (int i : whitelistedColumns) {
+          setColumnWhitelisted(i, true);
+        }
+      }
+    }
+
+    public boolean clearCurrentSelection(CellPreviewEvent<T> event) {
+      return false;
+    }
+
+    /**
+     * Clear all columns from the whitelist.
+     */
+    public void clearWhitelist() {
+      whitelist.clear();
+    }
+
+    /**
+     * Check if the specified column is whitelisted.
+     * 
+     * @param index the column index
+     * @return true if whitelisted, false if not
+     */
+    public boolean isColumnWhitelisted(int index) {
+      return whitelist.contains(index);
+    }
+
+    /**
+     * Set whether or not the specified column in whitelisted.
+     * 
+     * @param index the column index
+     * @param isWhitelisted true to whitelist, false to allow disallow selection
+     */
+    public void setColumnWhitelisted(int index, boolean isWhitelisted) {
+      if (isWhitelisted) {
+        whitelist.add(index);
+      } else {
+        whitelist.remove(index);
+      }
+    }
+
+    public SelectAction translateSelectionEvent(CellPreviewEvent<T> event) {
+      return isColumnWhitelisted(event.getColumn()) ? SelectAction.DEFAULT
+          : SelectAction.IGNORE;
+    }
+  }
+
+  /**
+   * Construct a new {@link DefaultSelectionEventManager} that ignores selection
+   * for the columns in the specified blacklist.
+   * 
+   * @param <T> the data type of the display
+   * @param blacklistedColumns the columns to include in the blacklist
+   * @return a {@link DefaultSelectionEventManager} instance
+   */
+  public static <T> DefaultSelectionEventManager<T> createBlacklistManager(
+      int... blacklistedColumns) {
+    return new DefaultSelectionEventManager<T>(new BlacklistEventTranslator<T>(
+        blacklistedColumns));
+  }
+  
+  /**
    * Construct a new {@link DefaultSelectionEventManager} that triggers
    * selection when any checkbox in any column is clicked.
    * 
@@ -183,6 +325,20 @@
   }
 
   /**
+   * Construct a new {@link DefaultSelectionEventManager} that allows selection
+   * only for the columns in the specified whitelist.
+   * 
+   * @param <T> the data type of the display
+   * @param whitelistedColumns the columns to include in the whitelist
+   * @return a {@link DefaultSelectionEventManager} instance
+   */
+  public static <T> DefaultSelectionEventManager<T> createWhitelistManager(
+      int... whitelistedColumns) {
+    return new DefaultSelectionEventManager<T>(new WhitelistEventTranslator<T>(
+        whitelistedColumns));
+  }
+
+  /**
    * The last {@link HasData} that was handled.
    */
   private HasData<T> lastDisplay;
diff --git a/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java b/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java
index c1cec44..9369a01 100644
--- a/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java
+++ b/user/test/com/google/gwt/view/client/DefaultSelectionEventManagerTest.java
@@ -19,7 +19,9 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.view.client.DefaultSelectionEventManager.BlacklistEventTranslator;
 import com.google.gwt.view.client.DefaultSelectionEventManager.SelectAction;
+import com.google.gwt.view.client.DefaultSelectionEventManager.WhitelistEventTranslator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -44,6 +46,29 @@
     return "com.google.gwt.view.View";
   }
 
+  public void testBlacklistEventTranslator() {
+    BlacklistEventTranslator<String> translator = new BlacklistEventTranslator<String>(
+        1, 3);
+    assertTrue(translator.isColumnBlacklisted(1));
+    assertFalse(translator.isColumnBlacklisted(2));
+    assertTrue(translator.isColumnBlacklisted(3));
+
+    translator.setColumnBlacklisted(2, true);
+    assertTrue(translator.isColumnBlacklisted(1));
+    assertTrue(translator.isColumnBlacklisted(2));
+    assertTrue(translator.isColumnBlacklisted(3));
+
+    translator.setColumnBlacklisted(2, false);
+    assertTrue(translator.isColumnBlacklisted(1));
+    assertFalse(translator.isColumnBlacklisted(2));
+    assertTrue(translator.isColumnBlacklisted(3));
+
+    translator.clearBlacklist();
+    assertFalse(translator.isColumnBlacklisted(1));
+    assertFalse(translator.isColumnBlacklisted(2));
+    assertFalse(translator.isColumnBlacklisted(3));
+  }
+
   public void testClearSelection() {
     MultiSelectionModel<String> model = new MultiSelectionModel<String>();
     model.setSelected("foo", true);
@@ -384,6 +409,29 @@
     assertSelected(model, "test 1");
   }
 
+  public void testWhitelistEventTranslator() {
+    WhitelistEventTranslator<String> translator = new WhitelistEventTranslator<String>(
+        1, 3);
+    assertTrue(translator.isColumnWhitelisted(1));
+    assertFalse(translator.isColumnWhitelisted(2));
+    assertTrue(translator.isColumnWhitelisted(3));
+
+    translator.setColumnWhitelisted(2, true);
+    assertTrue(translator.isColumnWhitelisted(1));
+    assertTrue(translator.isColumnWhitelisted(2));
+    assertTrue(translator.isColumnWhitelisted(3));
+
+    translator.setColumnWhitelisted(2, false);
+    assertTrue(translator.isColumnWhitelisted(1));
+    assertFalse(translator.isColumnWhitelisted(2));
+    assertTrue(translator.isColumnWhitelisted(3));
+
+    translator.clearWhitelist();
+    assertFalse(translator.isColumnWhitelisted(1));
+    assertFalse(translator.isColumnWhitelisted(2));
+    assertFalse(translator.isColumnWhitelisted(3));
+  }
+
   /**
    * Assert that the expected values are selected in the specified
    * {@link MultiSelectionModel}.