Fixes issue 792 by adding guards to detatch, also throws IllegalStateException if we ever try to attach when not attached, or detach when not detached.
Review by: jgw 

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@945 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index 2d4a2ee..5211ea2 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -75,10 +75,12 @@
    * It must not be overridden, except by {@link Panel}. To receive
    * notification when a widget is attached to the document, override the
    * {@link #onLoad} method.
+   * @throws IllegalStateException if this widget is already attached
    */
   protected void onAttach() {
     if (attached) {
-      return;
+      throw new IllegalStateException(
+          "Should only call onAttach when the widget is detached from the browser's document");
     }
 
     attached = true;
@@ -95,10 +97,13 @@
   /**
    * This method is called when a widget is detached from the browser's
    * document. It must not be overridden, except by {@link Panel}.
+   * 
+   * @throws IllegalStateException if this widget is already detached
    */
   protected void onDetach() {
     if (!attached) {
-      return;
+      throw new IllegalStateException(
+          "Should only call onDetach when the widget is attached to the browser's document");
     }
     attached = false;
 
@@ -116,32 +121,31 @@
   }
 
   /**
-    * Sets this object's browser element. Widget subclasses must call this
-    * method before attempting to call any other methods.
-    *
-    * If a browser element has already been attached, then it is replaced with
-    * the new element. The old event listeners are removed from the old browser
-    * element, and the event listeners are set up on the new browser element.
-    *
-    * @param elem the object's new element
-    */
-   protected void setElement(Element elem) {
-     if (attached) {
-       // Remove old event listener to avoid leaking. onDetach will not do this
-       // for us, because it is only called the widget itself is detached from
-       // the document.
-       DOM.setEventListener(getElement(), null);
-     }
+   * Sets this object's browser element. Widget subclasses must call this method
+   * before attempting to call any other methods.
+   * 
+   * If a browser element has already been attached, then it is replaced with
+   * the new element. The old event listeners are removed from the old browser
+   * element, and the event listeners are set up on the new browser element.
+   * 
+   * @param elem the object's new element
+   */
+  protected void setElement(Element elem) {
+    if (attached) {
+      // Remove old event listener to avoid leaking. onDetach will not do this
+      // for us, because it is only called when the widget itself is detached from
+      // the document.
+      DOM.setEventListener(getElement(), null);
+    }
 
-     super.setElement(elem);
-
-     if (attached) {
-       // Hook the event listener back up on the new element. onAttach will not
-       // do this for us, because it is only called when the widget itself is
-       // attached to the document.
-       DOM.setEventListener(elem, this);
-     }
-   }
+    super.setElement(elem);
+    if (attached) {
+      // Hook the event listener back up on the new element. onAttach will not
+      // do this for us, because it is only called when the widget itself is
+      // attached to the document.
+      DOM.setEventListener(elem, this);
+    }
+  }
 
   /**
    * Gets the panel-defined layout data associated with this widget.
@@ -172,10 +176,12 @@
    * @param parent the widget's new parent
    */
   void setParent(Widget parent) {
+    Widget oldParent = this.parent;
     this.parent = parent;
-
     if (parent == null) {
-      onDetach();
+      if (oldParent != null && oldParent.isAttached()) {
+        onDetach();
+      }
     } else if (parent.isAttached()) {
       onAttach();
     }