Replaced PopupPanel#setAutoHidePartner() with PopupPanel#addAutoHidePartner() so multiple partners can be specified.

Patch by: jlabanca
Review by: ecc
Issue: 3254



git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4394 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/VisualsForPopupEvents.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/VisualsForPopupEvents.java
index ffa898a..81fa475 100644
--- a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/VisualsForPopupEvents.java
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/VisualsForPopupEvents.java
@@ -24,12 +24,14 @@
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.PopupPanel;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
 
 /**
- * A simple tree used to quickly exercise tree behavior.
+ * Visual tests for {@link PopupPanel}.
  */
 public class VisualsForPopupEvents extends AbstractIssue {
   static int xPos = 0;
@@ -37,10 +39,26 @@
   @Override
   public Widget createIssue() {
     VerticalPanel panel = new VerticalPanel();
+    panel.setSpacing(3);
+    {
+      PopupPanel popup = createButton(true, false, panel);
+      Label partner0 = new Label("AutoHide Partner 0");
+      Label partner1 = new Label("AutoHide Partner 1");
+      popup.addAutoHidePartner(partner0.getElement());
+      popup.addAutoHidePartner(partner1.getElement());
+
+      HorizontalPanel hPanel = new HorizontalPanel();
+      hPanel.setBorderWidth(1);
+      hPanel.setSpacing(3);
+      hPanel.add(partner0);
+      hPanel.add(partner1);
+      panel.add(new Label(
+          "Clicking on partners should not autoHide this popup:"));
+      panel.add(hPanel);
+    }
     createButton(true, true, panel);
-    createButton(true, false, panel);
-    createButton(false, true, panel);
     createButton(false, false, panel);
+    createButton(false, true, panel);
     return panel;
   }
 
@@ -59,7 +77,8 @@
     return false;
   }
 
-  private void createButton(boolean autoHide, boolean modal, VerticalPanel panel) {
+  private PopupPanel createButton(boolean autoHide, boolean modal,
+      VerticalPanel panel) {
     final String text = " popup " + (modal ? " modal " : " non-modal ")
         + (autoHide ? "auto hide" : " persistent");
     panel.add(new HTML("<h2>" + text + "</h2>"));
@@ -85,5 +104,7 @@
             + event.isAutoClosed());
       }
     });
+
+    return p;
   }
 }
\ No newline at end of file
diff --git a/user/src/com/google/gwt/user/client/ui/PopupPanel.java b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
index 105b3be..6ed6891 100644
--- a/user/src/com/google/gwt/user/client/ui/PopupPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
@@ -34,6 +34,9 @@
 import com.google.gwt.user.client.Event.NativePreviewHandler;
 import com.google.gwt.user.client.ui.impl.PopupImpl;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A panel that can "pop up" over other widgets. It overlays the browser's
  * client area (and any previously-created popups).
@@ -280,7 +283,7 @@
 
   private boolean isAnimationEnabled = false;
 
-  private Element autoHidePartner;
+  private List<Element> autoHidePartners;
 
   /**
    * The {@link ResizeAnimation} used to open and close the {@link PopupPanel}s.
@@ -334,6 +337,20 @@
     this.modal = modal;
   }
 
+  /**
+   * Mouse events that occur within an autoHide partner will not hide a panel
+   * set to autoHide.
+   * 
+   * @param partner the auto hide partner to add
+   */
+  public void addAutoHidePartner(Element partner) {
+    assert partner != null : "partner cannot be null";
+    if (autoHidePartners == null) {
+      autoHidePartners = new ArrayList<Element>();
+    }
+    autoHidePartners.add(partner);
+  }
+
   public HandlerRegistration addCloseHandler(CloseHandler<PopupPanel> handler) {
     return addHandler(handler, CloseEvent.getType());
   }
@@ -536,6 +553,18 @@
     return true;
   }
 
+  /**
+   * Remove an autoHide partner.
+   * 
+   * @param partner the auto hide partner to remove
+   */
+  public void removeAutoHidePartner(Element partner) {
+    assert partner != null : "partner cannot be null";
+    if (autoHidePartners != null) {
+      autoHidePartners.remove(partner);
+    }
+  }
+
   @Deprecated
   public void removePopupListener(PopupListener listener) {
     ListenerWrapper.Popup.remove(this, listener);
@@ -556,16 +585,6 @@
   }
 
   /**
-   * If the auto hide partner is non null, its mouse events will not hide a
-   * panel set to autohide.
-   * 
-   * @param element new auto hide partner
-   */
-  public void setAutoHidePartner(Element element) {
-    this.autoHidePartner = element;
-  }
-
-  /**
    * Sets the height of the panel's child widget. If the panel's child widget
    * has not been set, the height passed in will be cached and used to set the
    * height immediately after the child widget is set.
@@ -834,14 +853,23 @@
   }-*/;
 
   /**
-   * Does the event target the partner element?
+   * Does the event target one of the partner elements?
    * 
    * @param event the native event
-   * @return true if the event targets the partner
+   * @return true if the event targets a partner
    */
   private boolean eventTargetsPartner(Event event) {
-    return autoHidePartner != null
-        && autoHidePartner.isOrHasChild(event.getTarget());
+    if (autoHidePartners == null) {
+      return false;
+    }
+
+    Element target = event.getTarget();
+    for (Element elem : autoHidePartners) {
+      if (elem.isOrHasChild(target)) {
+        return true;
+      }
+    }
+    return false;
   }
 
   /**
@@ -855,7 +883,7 @@
   }
 
   /**
-   * Get the element that {@link PopupImpl} uses.  PopupImpl creates an element
+   * Get the element that {@link PopupImpl} uses. PopupImpl creates an element
    * that goes inside of the outer element, so all methods in PopupImpl are
    * relative to the first child of the outer element, not the outer element
    * itself.
@@ -867,7 +895,8 @@
   }
 
   /**
-   * Positions the popup, called after the offset width and height of the popup are known.
+   * Positions the popup, called after the offset width and height of the popup
+   * are known.
    * 
    * @param relativeObject the ui object to position relative to
    * @param offsetWidth the drop down's offset width
diff --git a/user/src/com/google/gwt/user/datepicker/client/DateBox.java b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
index d9fc83d..4e5a6a4 100644
--- a/user/src/com/google/gwt/user/datepicker/client/DateBox.java
+++ b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
@@ -267,7 +267,7 @@
     this.format = format;
 
     popup.setAutoHideEnabled(true);
-    popup.setAutoHidePartner(box.getElement());
+    popup.addAutoHidePartner(box.getElement());
     popup.setWidget(picker);
     popup.setStyleName("dateBoxPopup");
 
diff --git a/user/test/com/google/gwt/user/client/ui/PopupTest.java b/user/test/com/google/gwt/user/client/ui/PopupTest.java
index 8aa91e3..4285ea6 100644
--- a/user/test/com/google/gwt/user/client/ui/PopupTest.java
+++ b/user/test/com/google/gwt/user/client/ui/PopupTest.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.user.client.ui;
 
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Document;
 import com.google.gwt.junit.client.GWTTestCase;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Timer;
@@ -82,6 +84,18 @@
     assertFalse(popup.isPreviewingAllNativeEvents());
   }
 
+  public void testAutoHidePartner() {
+    final PopupPanel popup = new PopupPanel();
+
+    // Add a partner
+    DivElement partner0 = Document.get().createDivElement();
+    popup.addAutoHidePartner(partner0);
+    popup.addAutoHidePartner(Document.get().createDivElement());
+
+    // Remove a partner
+    popup.removeAutoHidePartner(partner0);
+  }
+
   /**
    * Issue 2463: If a {@link PopupPanel} contains a dependent {@link PopupPanel}
    * that is hidden or shown in the onDetach or onAttach method, we could run