Description:
This patch makes events sink lazily on common GWT widgets. This is a  breaking change, as any subclasses which depend upon the events being sunk without adding listeners will no longer work.

Issue: 1491.
Review by: knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1780 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 7d6ebd7..72762f5 100644
--- a/user/src/com/google/gwt/user/client/ui/CheckBox.java
+++ b/user/src/com/google/gwt/user/client/ui/CheckBox.java
@@ -76,9 +76,6 @@
     inputElem = elem;
     labelElem = DOM.createLabel();
 
-    // Hook events to input widget rather than the check box element.
-    DOM.sinkEvents(inputElem, DOM.getEventsSunk(this.getElement()));
-    DOM.sinkEvents(this.getElement(), 0);
     DOM.appendChild(getElement(), inputElem);
     DOM.appendChild(getElement(), labelElem);
 
@@ -174,6 +171,13 @@
     DOM.setInnerText(labelElem, text);
   }
 
+  // Unlike other widgets the CheckBox sinks on its input element, not its
+  // wrapper element.
+  @Override
+  public void sinkEvents(int eventBitsToAdd) {
+    DOM.sinkEvents(inputElem, eventBitsToAdd | DOM.getEventsSunk(inputElem));
+  }
+
   /**
    * This method is called when a widget is attached to the browser's document.
    * onAttach needs special handling for the CheckBox case. Must still call
@@ -198,27 +202,26 @@
     DOM.setEventListener(inputElem, null);
     setChecked(isChecked());
   }
-  
+
   /**
    * Replace the current input element with a new one.
    * 
    * @param elem the new input element
    */
   protected void replaceInputElement(Element elem) {
+ 
     // Collect information we need to set
     int tabIndex = getTabIndex();
     boolean checked = isChecked();
     boolean enabled = isEnabled();
-    String uid = DOM.getElementProperty(inputElem, "id"); 
+    String uid = DOM.getElementProperty(inputElem, "id");
     String accessKey = DOM.getElementProperty(inputElem, "accessKey");
-    
+    int sunkEvents = DOM.getEventsSunk(inputElem);
+
     // Clear out the old input element
-    setChecked(false);
-    DOM.setElementProperty(inputElem, "id", "");
-    DOM.setElementProperty(inputElem, "accessKey", "");
     DOM.setEventListener(inputElem, null);
-    
-    // Quickly do the physical replace
+    DOM.setEventListener(inputElem, null);
+   
     DOM.removeChild(getElement(), inputElem);
     DOM.insertChild(getElement(), elem, 0);
 
@@ -228,6 +231,7 @@
     inputElem = elem;
 
     // Setup the new element
+    DOM.sinkEvents(inputElem, sunkEvents);
     DOM.setElementProperty(inputElem, "id", uid);
     if (accessKey != "") {
       DOM.setElementProperty(inputElem, "accessKey", accessKey);
diff --git a/user/src/com/google/gwt/user/client/ui/FocusWidget.java b/user/src/com/google/gwt/user/client/ui/FocusWidget.java
index c3df8f4..adef49b 100644
--- a/user/src/com/google/gwt/user/client/ui/FocusWidget.java
+++ b/user/src/com/google/gwt/user/client/ui/FocusWidget.java
@@ -24,7 +24,7 @@
  * Abstract base class for most widgets that can receive keyboard focus.
  */
 public abstract class FocusWidget extends Widget implements SourcesClickEvents,
-    SourcesFocusEvents, HasFocus {
+    SourcesFocusEvents, HasFocus, SourcesKeyboardEvents {
 
   private static final FocusImpl impl = FocusImpl.getFocusImplForWidget();
 
@@ -60,6 +60,7 @@
   public void addClickListener(ClickListener listener) {
     if (clickListeners == null) {
       clickListeners = new ClickListenerCollection();
+      sinkEvents(Event.ONCLICK);
     }
     clickListeners.add(listener);
   }
@@ -67,6 +68,7 @@
   public void addFocusListener(FocusListener listener) {
     if (focusListeners == null) {
       focusListeners = new FocusListenerCollection();
+      sinkEvents(Event.FOCUSEVENTS );
     }
     focusListeners.add(listener);
   }
@@ -74,6 +76,7 @@
   public void addKeyboardListener(KeyboardListener listener) {
     if (keyboardListeners == null) {
       keyboardListeners = new KeyboardListenerCollection();
+      sinkEvents(Event.KEYEVENTS);
     }
     keyboardListeners.add(listener);
   }
@@ -161,11 +164,6 @@
     impl.setTabIndex(getElement(), index);
   }
 
-  @Override
-  protected void setElement(Element elem) {
-    super.setElement(elem);
-    sinkEvents(Event.ONCLICK | Event.FOCUSEVENTS | Event.KEYEVENTS);
-  }
 
   /**
    * Fire all current {@link ClickListener}.
diff --git a/user/src/com/google/gwt/user/client/ui/HTML.java b/user/src/com/google/gwt/user/client/ui/HTML.java
index 04e8f2e..80edbc2 100644
--- a/user/src/com/google/gwt/user/client/ui/HTML.java
+++ b/user/src/com/google/gwt/user/client/ui/HTML.java
@@ -16,7 +16,6 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
 
 /**
  * A widget that can contain arbitrary HTML.
@@ -34,7 +33,8 @@
  * </ul>
  * 
  * <p>
- * <h3>Example</h3> {@example com.google.gwt.examples.HTMLExample}
+ * <h3>Example</h3>
+ * {@example com.google.gwt.examples.HTMLExample}
  * </p>
  */
 public class HTML extends Label implements HasHTML {
@@ -43,8 +43,7 @@
    * Creates an empty HTML widget.
    */
   public HTML() {
-    setElement(DOM.createDiv());
-    sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS);
+    super(DOM.createDiv());
     setStyleName("gwt-HTML");
   }
 
diff --git a/user/src/com/google/gwt/user/client/ui/HTMLTable.java b/user/src/com/google/gwt/user/client/ui/HTMLTable.java
index 014c6b2..3cf6093 100644
--- a/user/src/com/google/gwt/user/client/ui/HTMLTable.java
+++ b/user/src/com/google/gwt/user/client/ui/HTMLTable.java
@@ -769,7 +769,6 @@
     bodyElem = DOM.createTBody();
     DOM.appendChild(tableElem, bodyElem);
     setElement(tableElem);
-    sinkEvents(Event.ONCLICK);
   }
 
   /**
@@ -780,6 +779,7 @@
   public void addTableListener(TableListener listener) {
     if (tableListeners == null) {
       tableListeners = new TableListenerCollection();
+      sinkEvents(Event.ONCLICK);
     }
     tableListeners.add(listener);
   }
diff --git a/user/src/com/google/gwt/user/client/ui/Label.java b/user/src/com/google/gwt/user/client/ui/Label.java
index b59f5bd..45eb7d8 100644
--- a/user/src/com/google/gwt/user/client/ui/Label.java
+++ b/user/src/com/google/gwt/user/client/ui/Label.java
@@ -16,6 +16,7 @@
 package com.google.gwt.user.client.ui;
 
 import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 
 /**
@@ -45,11 +46,19 @@
    */
   public Label() {
     setElement(DOM.createDiv());
-    sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONMOUSEWHEEL);
     setStyleName("gwt-Label");
   }
 
   /**
+   * This constructor is used to let the HTML constructors avoid work.
+   * 
+   * @param element element
+   */
+  Label(Element element) {
+    setElement(element);
+  }
+
+  /**
    * Creates a label with the specified text.
    * 
    * @param text the new label's text
@@ -73,6 +82,7 @@
   public void addClickListener(ClickListener listener) {
     if (clickListeners == null) {
       clickListeners = new ClickListenerCollection();
+      sinkEvents(Event.ONCLICK);
     }
     clickListeners.add(listener);
   }
@@ -80,6 +90,7 @@
   public void addMouseListener(MouseListener listener) {
     if (mouseListeners == null) {
       mouseListeners = new MouseListenerCollection();
+      sinkEvents(Event.MOUSEEVENTS);
     }
     mouseListeners.add(listener);
   }
@@ -87,6 +98,7 @@
   public void addMouseWheelListener(MouseWheelListener listener) {
     if (mouseWheelListeners == null) {
       mouseWheelListeners = new MouseWheelListenerCollection();
+      sinkEvents(Event.ONMOUSEWHEEL);
     }
     mouseWheelListeners.add(listener);
   }
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 f0401b4..b9af87f 100644
--- a/user/src/com/google/gwt/user/client/ui/ListBox.java
+++ b/user/src/com/google/gwt/user/client/ui/ListBox.java
@@ -156,13 +156,13 @@
    */
   public ListBox(boolean isMultipleSelect) {
     super(DOM.createSelect(isMultipleSelect));
-    sinkEvents(Event.ONCHANGE);
     setStyleName("gwt-ListBox");
   }
 
   public void addChangeListener(ChangeListener listener) {
     if (changeListeners == null) {
       changeListeners = new ChangeListenerCollection();
+      sinkEvents(Event.ONCHANGE);
     }
     changeListeners.add(listener);
   }
diff --git a/user/src/com/google/gwt/user/client/ui/TextBoxBase.java b/user/src/com/google/gwt/user/client/ui/TextBoxBase.java
index b6fff33..5daea11 100644
--- a/user/src/com/google/gwt/user/client/ui/TextBoxBase.java
+++ b/user/src/com/google/gwt/user/client/ui/TextBoxBase.java
@@ -24,8 +24,8 @@
 /**
  * Abstract base class for all text entry widgets.
  */
-public class TextBoxBase extends FocusWidget implements SourcesKeyboardEvents,
-    SourcesChangeEvents, SourcesClickEvents, HasText, HasName {
+public class TextBoxBase extends FocusWidget implements SourcesChangeEvents,
+    HasText, HasName {
 
   /**
    * Text alignment constant, used in
@@ -70,9 +70,7 @@
   private static TextBoxImpl impl = GWT.create(TextBoxImpl.class);
 
   private ChangeListenerCollection changeListeners;
-  private ClickListenerCollection clickListeners;
   private Event currentEvent;
-  private KeyboardListenerCollection keyboardListeners;
 
   /**
    * Creates a text box that wraps the given browser element handle. This is
@@ -82,32 +80,16 @@
    */
   protected TextBoxBase(Element elem) {
     super(elem);
-    sinkEvents(Event.ONCHANGE);
   }
 
   public void addChangeListener(ChangeListener listener) {
     if (changeListeners == null) {
       changeListeners = new ChangeListenerCollection();
+      sinkEvents(Event.ONCHANGE);
     }
     changeListeners.add(listener);
   }
 
-  @Override
-  public void addClickListener(ClickListener listener) {
-    if (clickListeners == null) {
-      clickListeners = new ClickListenerCollection();
-    }
-    clickListeners.add(listener);
-  }
-
-  @Override
-  public void addKeyboardListener(KeyboardListener listener) {
-    if (keyboardListeners == null) {
-      keyboardListeners = new KeyboardListenerCollection();
-    }
-    keyboardListeners.add(listener);
-  }
-
   /**
    * If a keyboard event is currently being handled on this text box, calling
    * this method will suppress it. This allows listeners to easily filter
@@ -168,27 +150,23 @@
 
   @Override
   public void onBrowserEvent(Event event) {
-    // Call the superclass' implementation first (because FocusWidget fires
-    // some events itself).
-    super.onBrowserEvent(event);
-
     int type = DOM.eventGetType(event);
-    if ((keyboardListeners != null) && (type & Event.KEYEVENTS) != 0) {
+    if ((type & Event.KEYEVENTS) != 0) {
       // Fire the keyboard event. Hang on to the current event object so that
       // cancelKey() and setKey() can be implemented.
       currentEvent = event;
-      keyboardListeners.fireKeyboardEvent(this, event);
+      // Call the superclass' onBrowserEvent as that includes the key event
+      // handlers.
+      super.onBrowserEvent(event);
       currentEvent = null;
-    } else if (type == Event.ONCLICK) {
-      // Fire the click event.
-      if (clickListeners != null) {
-        clickListeners.fireClick(this);
-      }
     } else if (type == Event.ONCHANGE) {
       // Fire the change event.
       if (changeListeners != null) {
         changeListeners.fireChange(this);
       }
+    } else {
+      // Handles Focus and Click events.
+      super.onBrowserEvent(event);
     }
   }
 
@@ -198,20 +176,6 @@
     }
   }
 
-  @Override
-  public void removeClickListener(ClickListener listener) {
-    if (clickListeners != null) {
-      clickListeners.remove(listener);
-    }
-  }
-
-  @Override
-  public void removeKeyboardListener(KeyboardListener listener) {
-    if (keyboardListeners != null) {
-      keyboardListeners.remove(listener);
-    }
-  }
-
   /**
    * Selects all of the text in the box.
    */