Fix for issue 1112 (Add InsertPanel interface)
Review: http://gwt-code-reviews.appspot.com/57808

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6070 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/AbsolutePanel.java b/user/src/com/google/gwt/user/client/ui/AbsolutePanel.java
index 188dc6a..0cd3fc1 100644
--- a/user/src/com/google/gwt/user/client/ui/AbsolutePanel.java
+++ b/user/src/com/google/gwt/user/client/ui/AbsolutePanel.java
@@ -34,7 +34,7 @@
  * the widget may be modified by the panel.
  * </p>
  */
-public class AbsolutePanel extends ComplexPanel {
+public class AbsolutePanel extends ComplexPanel implements InsertPanel {
 
   /**
    * Changes a DOM element's positioning to static.
@@ -70,11 +70,6 @@
     setElement(elem);
   }
 
-  /**
-   * Adds a child widget to this panel.
-   * 
-   * @param w the child widget to be added
-   */
   @Override
   public void add(Widget w) {
     super.add(w, getElement());
@@ -95,8 +90,9 @@
     // The Widget should be removed from its parent before any positional
     // changes are made to prevent flickering.
     w.removeFromParent();
+    int beforeIndex = getWidgetCount();
     setWidgetPositionImpl(w, left, top);
-    add(w);
+    insert(w, beforeIndex);
   }
 
   /**
@@ -125,6 +121,33 @@
         - DOM.getAbsoluteTop(getElement());
   }
 
+  public void insert(Widget w, int beforeIndex) {
+    insert(w, getElement(), beforeIndex, true);
+  }
+
+  /**
+   * Inserts a child widget at the specified position before the specified
+   * index. Setting a position of <code>(-1, -1)</code> will cause the child
+   * widget to be positioned statically. If the widget is already a child of
+   * this panel, it will be moved to the specified index.
+   * 
+   * @param w the child widget to be inserted
+   * @param left the widget's left position
+   * @param top the widget's top position
+   * @param beforeIndex the index before which it will be inserted
+   * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of
+   *           range
+   */
+  public void insert(Widget w, int left, int top, int beforeIndex) {
+    // In order to avoid the potential for a flicker effect, it is necessary
+    // to set the position of the widget before adding it to the AbsolutePanel.
+    // The Widget should be removed from its parent before any positional
+    // changes are made to prevent flickering.
+    w.removeFromParent();
+    setWidgetPositionImpl(w, left, top);
+    insert(w, beforeIndex);
+  }
+
   /**
    * Overrides {@link ComplexPanel#remove(Widget)} to change the removed
    * Widget's element back to static positioning.This is done so that any
diff --git a/user/src/com/google/gwt/user/client/ui/DeckPanel.java b/user/src/com/google/gwt/user/client/ui/DeckPanel.java
index 8ce725d..0126171 100644
--- a/user/src/com/google/gwt/user/client/ui/DeckPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DeckPanel.java
@@ -31,7 +31,7 @@
  * cleared.
  * </p>
  */
-public class DeckPanel extends ComplexPanel implements HasAnimation {
+public class DeckPanel extends ComplexPanel implements HasAnimation, InsertPanel {
   /**
    * The duration of the animation.
    */
@@ -244,11 +244,6 @@
     setElement(DOM.createDiv());
   }
 
-  /**
-   * Adds the specified widget to the deck.
-   * 
-   * @param w the widget to be added
-   */
   @Override
   public void add(Widget w) {
     Element container = createWidgetContainer();
@@ -276,14 +271,6 @@
     return getWidgetIndex(visibleWidget);
   }
 
-  /**
-   * Inserts a widget before the specified index.
-   * 
-   * @param w the widget to be inserted
-   * @param beforeIndex the index before which it will be inserted
-   * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of
-   *           range
-   */
   public void insert(Widget w, int beforeIndex) {
     Element container = createWidgetContainer();
     DOM.insertChild(getElement(), container, beforeIndex);
diff --git a/user/src/com/google/gwt/user/client/ui/FlowPanel.java b/user/src/com/google/gwt/user/client/ui/FlowPanel.java
index 9da634d..d27c74c 100644
--- a/user/src/com/google/gwt/user/client/ui/FlowPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/FlowPanel.java
@@ -26,7 +26,7 @@
  * <img class='gallery' src='FlowPanel.png'/>
  * </p>
  */
-public class FlowPanel extends ComplexPanel {
+public class FlowPanel extends ComplexPanel implements InsertPanel {
 
   /**
    * Creates an empty flow panel.
diff --git a/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java b/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
index 21b555a..8071ce6 100644
--- a/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
@@ -25,7 +25,8 @@
  * <img class='gallery' src='HorizontalPanel.png'/>
  * </p>
  */
-public class HorizontalPanel extends CellPanel implements HasAlignment {
+public class HorizontalPanel extends CellPanel implements HasAlignment,
+    InsertPanel {
 
   private HorizontalAlignmentConstant horzAlign = ALIGN_DEFAULT;
   private Element tableRow;
@@ -42,12 +43,6 @@
     DOM.setElementProperty(getTable(), "cellPadding", "0");
   }
 
-  /**
-   * Adds a child widget to the panel. If the Widget is already attached to the
-   * HorizontalPanel, it will be moved to the end of the panel.
-   * 
-   * @param w the widget to be added
-   */
   @Override
   public void add(Widget w) {
     Element td = createAlignedTd();
@@ -63,15 +58,6 @@
     return vertAlign;
   }
 
-  /**
-   * Inserts a widget before the specified index. If the Widget is already
-   * attached to the HorizontalPanel, it will be moved to the specified index.
-   * 
-   * @param w the widget to be inserted
-   * @param beforeIndex the index before which it will be inserted
-   * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of
-   *           range
-   */
   public void insert(Widget w, int beforeIndex) {
     checkIndexBoundsForInsertion(beforeIndex);
 
diff --git a/user/src/com/google/gwt/user/client/ui/StackPanel.java b/user/src/com/google/gwt/user/client/ui/StackPanel.java
index feb0cc7..64b49ef 100644
--- a/user/src/com/google/gwt/user/client/ui/StackPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/StackPanel.java
@@ -38,7 +38,7 @@
  * {@example com.google.gwt.examples.StackPanelExample}
  * </p>
  */
-public class StackPanel extends ComplexPanel {
+public class StackPanel extends ComplexPanel implements InsertPanel {
   private static final String DEFAULT_STYLENAME = "gwt-StackPanel";
   private static final String DEFAULT_ITEM_STYLENAME = DEFAULT_STYLENAME
       + "Item";
@@ -62,11 +62,6 @@
     setStyleName(DEFAULT_STYLENAME);
   }
 
-  /**
-   * Adds a new child with the given widget.
-   * 
-   * @param w the widget to be added
-   */
   @Override
   public void add(Widget w) {
     insert(w, getWidgetCount());
@@ -104,14 +99,6 @@
     return visibleStack;
   }
 
-  /**
-   * Inserts a widget before the specified index.
-   * 
-   * @param w the widget to be inserted
-   * @param beforeIndex the index before which it will be inserted
-   * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of
-   *           range
-   */
   public void insert(Widget w, int beforeIndex) {
     // header
     Element trh = DOM.createTR();
diff --git a/user/src/com/google/gwt/user/client/ui/VerticalPanel.java b/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
index f9bacd2..2bf33e8 100644
--- a/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
@@ -25,7 +25,8 @@
  * <img class='gallery' src='VerticalPanel.png'/>
  * </p>
  */
-public class VerticalPanel extends CellPanel implements HasAlignment {
+public class VerticalPanel extends CellPanel implements HasAlignment,
+    InsertPanel {
 
   private HorizontalAlignmentConstant horzAlign = ALIGN_DEFAULT;
   private VerticalAlignmentConstant vertAlign = ALIGN_TOP;
@@ -38,12 +39,6 @@
     DOM.setElementProperty(getTable(), "cellPadding", "0");
   }
 
-  /**
-   * Adds a child widget to the panel. If the Widget is already attached to the
-   * VerticalPanel, it will be moved to the end of the panel.
-   * 
-   * @param w the widget to be added
-   */
   @Override
   public void add(Widget w) {
     Element tr = DOM.createTR();
@@ -61,21 +56,13 @@
     return vertAlign;
   }
 
-  /**
-   * Inserts a widget before the specified index. If the Widget is already
-   * attached to the VerticalPanel, it will be moved to the specified index.
-   * 
-   * @param w the widget to be inserted
-   * @param beforeIndex the index before which it will be inserted
-   * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of
-   *           range
-   */
   public void insert(Widget w, int beforeIndex) {
     checkIndexBoundsForInsertion(beforeIndex);
 
     Element tr = DOM.createTR();
     Element td = createAlignedTd();
     DOM.appendChild(tr, td);
+
     /*
      * The case where we reinsert an already existing child is tricky.
      * 
diff --git a/user/test/com/google/gwt/user/client/ui/AbsolutePanelTest.java b/user/test/com/google/gwt/user/client/ui/AbsolutePanelTest.java
index ff5879c..0d2da55 100644
--- a/user/test/com/google/gwt/user/client/ui/AbsolutePanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/AbsolutePanelTest.java
@@ -22,7 +22,7 @@
 import com.google.gwt.user.client.Window;
 
 /**
- * TODO: document me.
+ * Tests for {@link AbsolutePanel}.
  */
 public class AbsolutePanelTest extends GWTTestCase {
 
@@ -34,6 +34,19 @@
     HasWidgetsTester.testAll(new AbsolutePanel());
   }
 
+  /**
+   * AbsolutePanel once had a bug where calling
+   * {@link AbsolutePanel#add(Widget, int, int)} twice on the same child widget
+   * would throw an {@link IndexOutOfBoundsException}.
+   */
+  public void testDoubleAdd() {
+    AbsolutePanel absolutePanel = new AbsolutePanel();
+    Label label = new Label("label");
+
+    absolutePanel.add(label, 10, 10);
+    absolutePanel.add(label, 10, 10);
+  }
+  
   @DoNotRunWith(Platform.Htmlunit)
   public void testPositioning() {
     // Make an absolute panel with a label at (3, 7).