Let ToggleButton implement HasValue<Boolean>
http://gwt-code-reviews.appspot.com/887804


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8982 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/CheckBox.java b/user/src/com/google/gwt/user/client/ui/CheckBox.java
index a0ed806..75635ee 100644
--- a/user/src/com/google/gwt/user/client/ui/CheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -71,7 +71,7 @@
 
   /**
    * Creates a check box with the specified text label.
-   *
+   * 
    * @param label the check box's label
    */
   public CheckBox(SafeHtml label) {
@@ -80,7 +80,7 @@
 
   /**
    * Creates a check box with the specified text label.
-   *
+   * 
    * @param label the check box's label
    */
   public CheckBox(String label) {
@@ -170,14 +170,14 @@
   }
 
   /**
-   * Determines whether this check box is currently checked. 
+   * Determines whether this check box is currently checked.
    * <p>
    * Note that this <em>is not</em> return the value property of the checkbox
    * input element wrapped by this widget. For access to that property, see
    * {@link #getFormValue()}
    * 
    * @return <code>true</code> if the check box is checked, false otherwise.
-   * Will not return null
+   *         Will not return null
    */
   public Boolean getValue() {
     if (isAttached()) {
@@ -200,7 +200,7 @@
   @Deprecated
   public boolean isChecked() {
     // Funny comparison b/c getValue could in theory return null
-    return getValue() == true; 
+    return getValue() == true;
   }
 
   @Override
@@ -285,31 +285,29 @@
   }
 
   /**
-   * Checks or unchecks the text box.
+   * Checks or unchecks the check box.
    * <p>
    * Note that this <em>does not</em> set the value property of the checkbox
    * input element wrapped by this widget. For access to that property, see
    * {@link #setFormValue(String)}
    * 
-   * @param value true to check, false to uncheck; must not be null
-   * @throws IllegalArgumentException if value is null
+   * @param value true to check, false to uncheck; null value implies false
    */
   public void setValue(Boolean value) {
     setValue(value, false);
   }
 
   /**
-   * Checks or unchecks the text box, firing {@link ValueChangeEvent} if
+   * Checks or unchecks the check box, firing {@link ValueChangeEvent} if
    * appropriate.
    * <p>
    * Note that this <em>does not</em> set the value property of the checkbox
    * input element wrapped by this widget. For access to that property, see
    * {@link #setFormValue(String)}
-   *
+   * 
    * @param value true to check, false to uncheck; null value implies false
    * @param fireEvents If true, and value has changed, fire a
    *          {@link ValueChangeEvent}
-   * @throws IllegalArgumentException if value is null
    */
   public void setValue(Boolean value, boolean fireEvents) {
     if (value == null) {
@@ -337,8 +335,8 @@
   @Override
   public void sinkEvents(int eventBitsToAdd) {
     if (isOrWasAttached()) {
-      Event.sinkEvents(inputElem, 
-          eventBitsToAdd | Event.getEventsSunk(inputElem));
+      Event.sinkEvents(inputElem, eventBitsToAdd
+          | Event.getEventsSunk(inputElem));
     } else {
       super.sinkEvents(eventBitsToAdd);
     }
@@ -395,10 +393,9 @@
   }
 
   /**
-   * Replace the current input element with a new one. Preserves
-   * all state except for the name property, for nasty reasons
-   * related to radio button grouping. (See implementation of 
-   * {@link RadioButton#setName}.)
+   * Replace the current input element with a new one. Preserves all state
+   * except for the name property, for nasty reasons related to radio button
+   * grouping. (See implementation of {@link RadioButton#setName}.)
    * 
    * @param elem the new input element
    */
@@ -411,7 +408,7 @@
     String formValue = getFormValue();
     String uid = inputElem.getId();
     String accessKey = inputElem.getAccessKey();
-    int sunkEvents = Event.getEventsSunk(inputElem);   
+    int sunkEvents = Event.getEventsSunk(inputElem);
 
     // Clear out the old input element
     setEventListener(asOld(inputElem), null);
diff --git a/user/src/com/google/gwt/user/client/ui/ToggleButton.java b/user/src/com/google/gwt/user/client/ui/ToggleButton.java
index b80b8e8..3ce977b 100644
--- a/user/src/com/google/gwt/user/client/ui/ToggleButton.java
+++ b/user/src/com/google/gwt/user/client/ui/ToggleButton.java
@@ -17,6 +17,9 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
 
 /**
  * A <code>ToggleButton</code> is a stylish stateful button which allows the
@@ -26,15 +29,18 @@
  * <img class='gallery' src='doc-files/ToggleButton.png'/>
  * </p>
  * 
- * <h3>CSS Style Rules</h3> <ul class="css"> <li>
+ * <h3>CSS Style Rules</h3>
+ * <ul class="css">
+ * <li>
  * .gwt-ToggleButton-up/down/up-hovering/down-hovering/up-disabled/down-disabled
- * {.html-face}</li> </ul>
+ * {.html-face}</li>
+ * </ul>
  * 
  * <p>
  * <h3>Example</h3> {@example com.google.gwt.examples.ToggleButtonExample}
  * </p>
  */
-public class ToggleButton extends CustomButton {
+public class ToggleButton extends CustomButton implements HasValue<Boolean> {
   private static String STYLENAME_DEFAULT = "gwt-ToggleButton";
 
   {
@@ -63,12 +69,10 @@
    * construct the default face of the button.
    * 
    * @param upImage image for the default (up) face of the button
-   * @param listener the click listener
-   * @deprecated Use {@link #ToggleButton(Image, ClickHandler)} instead
+   * @param handler the click handler
    */
-  @Deprecated
-  public ToggleButton(Image upImage, ClickListener listener) {
-    super(upImage, listener);
+  public ToggleButton(Image upImage, ClickHandler handler) {
+    super(upImage, handler);
   }
 
   /**
@@ -76,10 +80,12 @@
    * construct the default face of the button.
    * 
    * @param upImage image for the default (up) face of the button
-   * @param handler the click handler
+   * @param listener the click listener
+   * @deprecated Use {@link #ToggleButton(Image, ClickHandler)} instead
    */
-  public ToggleButton(Image upImage, ClickHandler handler) {
-    super(upImage, handler);
+  @Deprecated
+  public ToggleButton(Image upImage, ClickListener listener) {
+    super(upImage, listener);
   }
 
   /**
@@ -97,12 +103,10 @@
    * 
    * @param upImage image for the default(up) face of the button
    * @param downImage image for the down face of the button
-   * @param listener clickListener
-   * @deprecated Use {@link #ToggleButton(Image, Image, ClickHandler)} instead
+   * @param handler the click handler
    */
-  @Deprecated
-  public ToggleButton(Image upImage, Image downImage, ClickListener listener) {
-    super(upImage, downImage, listener);
+  public ToggleButton(Image upImage, Image downImage, ClickHandler handler) {
+    super(upImage, downImage, handler);
   }
 
   /**
@@ -110,10 +114,12 @@
    * 
    * @param upImage image for the default(up) face of the button
    * @param downImage image for the down face of the button
-   * @param handler the click handler
+   * @param listener clickListener
+   * @deprecated Use {@link #ToggleButton(Image, Image, ClickHandler)} instead
    */
-  public ToggleButton(Image upImage, Image downImage, ClickHandler handler) {
-    super(upImage, downImage, handler);
+  @Deprecated
+  public ToggleButton(Image upImage, Image downImage, ClickListener listener) {
+    super(upImage, downImage, listener);
   }
 
   /**
@@ -131,12 +137,10 @@
    * construct the default face of the button.
    * 
    * @param upText the text for the default (up) face of the button
-   * @param listener the click listener
-   * @deprecated Use {@link #ToggleButton(String, ClickHandler)} instead
+   * @param handler the click handler
    */
-  @Deprecated
-  public ToggleButton(String upText, ClickListener listener) {
-    super(upText, listener);
+  public ToggleButton(String upText, ClickHandler handler) {
+    super(upText, handler);
   }
 
   /**
@@ -144,10 +148,12 @@
    * construct the default face of the button.
    * 
    * @param upText the text for the default (up) face of the button
-   * @param handler the click handler
+   * @param listener the click listener
+   * @deprecated Use {@link #ToggleButton(String, ClickHandler)} instead
    */
-  public ToggleButton(String upText, ClickHandler handler) {
-    super(upText, handler);
+  @Deprecated
+  public ToggleButton(String upText, ClickListener listener) {
+    super(upText, listener);
   }
 
   /**
@@ -160,21 +166,82 @@
     super(upText, downText);
   }
 
+  /**
+   * Constructor for <code>ToggleButton</code>.
+   * 
+   * @param upText the text for the default (up) face of the button
+   * @param downText the text for down face of the button
+   * @param handler the click handler
+   */
+  public ToggleButton(String upText, String downText, ClickHandler handler) {
+    super(upText, downText, handler);
+  }
+
+  public HandlerRegistration addValueChangeHandler(
+      ValueChangeHandler<Boolean> handler) {
+    return addHandler(handler, ValueChangeEvent.getType());
+  }
+
+  /**
+   * Determines whether this button is currently down.
+   * 
+   * @return <code>true</code> if the button is pressed, false otherwise. Will
+   *         not return null
+   */
+  public Boolean getValue() {
+    return isDown();
+  }
+
   @Override
   public boolean isDown() {
     // Changes access to public.
     return super.isDown();
   }
 
+  /**
+   * {@inheritDoc} Does not fire {@link ValueChangeEvent}. (If you want the
+   * event to fire, use {@link #setValue(Boolean, boolean)})
+   */
   @Override
   public void setDown(boolean down) {
     // Changes access to public.
     super.setDown(down);
   }
 
+  /**
+   * Sets whether this button is down.
+   * 
+   * @param value true to press the button, false otherwise; null value implies
+   *          false
+   */
+  public void setValue(Boolean value) {
+    setValue(value, false);
+  }
+
+  /**
+   * Sets whether this button is down, firing {@link ValueChangeEvent} if
+   * appropriate.
+   * 
+   * @param value true to press the button, false otherwise; null value implies
+   *          false
+   * @param fireEvents If true, and value has changed, fire a
+   *          {@link ValueChangeEvent}
+   */
+  public void setValue(Boolean value, boolean fireEvents) {
+    if (value == null) {
+      value = Boolean.FALSE;
+    }
+    boolean oldValue = isDown();
+    setDown(value);
+    if (fireEvents) {
+      ValueChangeEvent.fireIfNotEqual(this, oldValue, value);
+    }
+  }
+
   @Override
   protected void onClick() {
     toggleDown();
     super.onClick();
+    ValueChangeEvent.fire(this, isDown());
   }
 }
diff --git a/user/test/com/google/gwt/user/client/ui/CustomButtonTest.java b/user/test/com/google/gwt/user/client/ui/CustomButtonTest.java
index 75a76cc..6f38b6e 100644
--- a/user/test/com/google/gwt/user/client/ui/CustomButtonTest.java
+++ b/user/test/com/google/gwt/user/client/ui/CustomButtonTest.java
@@ -19,6 +19,8 @@
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.safehtml.shared.SafeHtmlUtils;
 import com.google.gwt.user.client.DOM;
@@ -38,6 +40,14 @@
  */
 public class CustomButtonTest extends GWTTestCase {
 
+  private static class Handler implements ValueChangeHandler<Boolean> {
+    Boolean received = null;
+    
+    public void onValueChange(ValueChangeEvent<Boolean> event) {
+      received = event.getValue();
+    }
+  }
+
   private static final String html = "<b>hello</b><i>world</i>";
 
   public String getModuleName() {
@@ -159,14 +169,16 @@
   }
 
   public void testSyntheticClick() {
-    PushButton b = new PushButton();
+    ToggleButton b = new ToggleButton();
     final ArrayList<String> events = new ArrayList<String>();
+    Handler h = new Handler();
 
     b.addClickHandler(new ClickHandler() {
       public void onClick(ClickEvent event) {
         events.add(event.getNativeEvent().getType());
       }
     });
+    b.addValueChangeHandler(h);
 
     RootPanel.get().add(b);
 
@@ -183,6 +195,7 @@
             false, Event.BUTTON_LEFT));
     assertEquals("Expecting one click event", 1, events.size());
     assertEquals("Expecting one click event", "click", events.get(0));
+    assertNotNull("Expecting a value change event", h.received);
   }
   
   public void testTransitions() {
@@ -219,4 +232,28 @@
     assertFalse(b.isDown());
     assertFalse(b.isEnabled());
   }
+
+  public void testToogleButtonHasValue() {
+    ToggleButton tb = new ToggleButton();
+    Handler h = new Handler();
+    tb.addValueChangeHandler(h);
+    tb.setDown(false);
+    assertNull(h.received);
+    tb.setDown(true);
+    assertNull(h.received);
+
+    tb.setValue(false);
+    assertNull(h.received);
+    tb.setValue(true);
+    assertNull(h.received);
+
+    tb.setValue(true, true);
+    assertNull(h.received);
+
+    tb.setValue(false, true);
+    assertFalse(h.received);
+
+    tb.setValue(true, true);
+    assertTrue(h.received);
+  }
 }