Fixes issue #950 "Can not select multiple items on Listbox programmatically"

Review by: tobyr, bruce (pair programmed and desk reviewed)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1006 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/DOM.java b/user/src/com/google/gwt/user/client/DOM.java
index 0f5c974..d9a0bae 100644
--- a/user/src/com/google/gwt/user/client/DOM.java
+++ b/user/src/com/google/gwt/user/client/DOM.java
@@ -244,12 +244,24 @@
   }
 
   /**
-   * Creates an HTML SELECT element.
+   * Creates a single-selection HTML SELECT element. Equivalent to 
+   * <pre>
+   * createSelect(false)
+   * </pre>
    *
    * @return the newly-created element
    */
   public static Element createSelect() {
-    return impl.createElement("select");
+    return DOM.createSelect(false);
+  }
+
+  /**
+   * Creates an HTML SELECT element.
+   *
+   * @return the newly-created element
+   */
+  public static Element createSelect(boolean multiple) {
+    return impl.createSelectElement(multiple);
   }
 
   /**
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImpl.java b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
index 4069ab9..c6b4f88 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -43,6 +43,14 @@
 
   public abstract Element createInputRadioElement(String group);
 
+  public Element createSelectElement(boolean multiple) {
+    Element select = createElement("select");
+    if (multiple) {
+      setElementPropertyBoolean(select, "multiple", true);
+    }
+    return select;
+  }
+
   public native void eventCancelBubble(Event evt, boolean cancel) /*-{
     evt.cancelBubble = cancel;
   }-*/;
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
index 4fd4a2f..f3094da 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
@@ -36,6 +36,18 @@
     return $doc.createElement("<INPUT type='RADIO' name='" + group + "'>");
   }-*/;
 
+  /**
+   * Supports creating a select control with the multiple attribute to work
+   * around a bug in IE6 where changing the multiple attribute in a
+   * setAttribute call can cause subsequent setSelected calls to misbehave.
+   * Although this bug is fixed in IE7, this DOMImpl specialization is used
+   * for both IE6 and IE7, but it should be harmless.
+   */
+  public native Element createSelectElement(boolean multiple) /*-{
+    var html = multiple ? "<SELECT MULTIPLE>" : "<SELECT>"; 
+    return $doc.createElement(html);
+  }-*/;
+
   public native int eventGetMouseWheelVelocityY(Event evt) /*-{
     return -evt.wheelDelta / 40;
   }-*/;
diff --git a/user/src/com/google/gwt/user/client/ui/ListBox.java b/user/src/com/google/gwt/user/client/ui/ListBox.java
index 36fec34..d26b083 100644
--- a/user/src/com/google/gwt/user/client/ui/ListBox.java
+++ b/user/src/com/google/gwt/user/client/ui/ListBox.java
@@ -44,10 +44,20 @@
   private ChangeListenerCollection changeListeners;
 
   /**
-   * Creates an empty list box.
+   * Creates an empty list box in single selection mode.
    */
   public ListBox() {
-    super(DOM.createSelect());
+    this(false);
+  }
+
+  /**
+   * Creates an empty list box. The preferred way to enable multiple selections
+   * is to use this constructor rather than {@link #setMultipleSelect(boolean)}.
+   * 
+   * @param isMultipleSelect specifies if multiple selection is enabled
+   */
+  public ListBox(boolean isMultipleSelect) {
+    super(DOM.createSelect(isMultipleSelect));
     sinkEvents(Event.ONCHANGE);
     setStyleName("gwt-ListBox");
   }
@@ -60,7 +70,8 @@
   }
 
   /**
-   * Adds an item to the list box. This method has the same effect as 
+   * Adds an item to the list box. This method has the same effect as
+   * 
    * <pre>
    * addItem(item, item)
    * </pre>
@@ -128,7 +139,7 @@
   }
 
   /**
-   * Gets the value associated with the item at a given index. 
+   * Gets the value associated with the item at a given index.
    * 
    * @param index the index of the item to be retrieved
    * @return the item's associated value
@@ -152,7 +163,8 @@
   }
 
   /**
-   * Inserts an item into the list box. Has the same effect as 
+   * Inserts an item into the list box. Has the same effect as
+   * 
    * <pre>
    * insertItem(item, item, index)
    * </pre>
@@ -165,7 +177,8 @@
   }
 
   /**
-   * Inserts an item into the list box, specifying an initial value for the item.
+   * Inserts an item into the list box, specifying an initial value for the
+   * item.
    * 
    * @param item the text of the item to be inserted
    * @param value the item's value, to be submitted if it is part of a
@@ -253,11 +266,15 @@
   }
 
   /**
-   * Sets whether this list allows multiple selections.
+   * Sets whether this list allows multiple selections. <em>NOTE: The preferred
+   * way of enabling multiple selections in a list box is by using the
+   * {@link #ListBox(boolean)} constructor. Using this method can spuriously
+   * fail on Internet Explorer 6.0.</em>
    * 
    * @param multiple <code>true</code> to allow multiple selections
    */
   public void setMultipleSelect(boolean multiple) {
+    // TODO: we can remove the above doc admonition once we address issue 1007
     DOM.setElementPropertyBoolean(getElement(), "multiple", multiple);
   }
 
diff --git a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
index 8ad2489..25ca950 100644
--- a/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ListBoxTest.java
@@ -37,12 +37,31 @@
   }
 
   public void testSelection() {
-    ListBox box = new ListBox();
-    box.addItem("a");
-    box.setSelectedIndex(-1);
-    assertEquals(-1, box.getSelectedIndex());
-    box.setSelectedIndex(0);
-    assertEquals("a", box.getItemText(box.getSelectedIndex()));
+    {
+      ListBox box = new ListBox();
+      box.addItem("a");
+      box.setSelectedIndex(-1);
+      assertEquals(-1, box.getSelectedIndex());
+      box.setSelectedIndex(0);
+      assertEquals("a", box.getItemText(box.getSelectedIndex()));
+    }
+
+    // Testing multiple selection
+    {
+      ListBox box = new ListBox(true);
+      box.setMultipleSelect(true);
+      box.addItem("a");
+      box.addItem("b");
+      box.addItem("c");
+
+      for (int j = 0; j < box.getItemCount(); j++) {
+        box.setItemSelected(j, true);
+      }
+
+      for (int j = 0; j < box.getItemCount(); j++) {
+        assertTrue(box.isItemSelected(j));
+      }
+    }
   }
 
   public void testSetStyleNames() {