Fixes issue #616; refactores ComplexPanel to just implement IndexedPanel.  So all ComplexPanel subclass now implement it as well.  This is nice because it allows a lot of shared code to be merged into ComplexPanel.java

Suggested by: fredsa
Review by: jgw, knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1050 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/ComplexPanel.java b/user/src/com/google/gwt/user/client/ui/ComplexPanel.java
index e6cdc6c..4ac2ab9 100644
--- a/user/src/com/google/gwt/user/client/ui/ComplexPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/ComplexPanel.java
@@ -22,24 +22,40 @@
 /**
  * Abstract base class for panels that can contain multiple child widgets.
  */
-public abstract class ComplexPanel extends Panel {
+public abstract class ComplexPanel extends Panel implements IndexedPanel {
 
   private WidgetCollection children = new WidgetCollection(this);
 
+  public Widget getWidget(int index) {
+    return getChildren().get(index);
+  }
+
+  public int getWidgetCount() {
+    return getChildren().size();
+  }
+
+  public int getWidgetIndex(Widget child) {
+    return getChildren().indexOf(child);
+  }
+
   public Iterator iterator() {
-    return children.iterator();
+    return getChildren().iterator();
+  }
+
+  public boolean remove(int index) {
+    return remove(getWidget(index));
   }
 
   public boolean remove(Widget w) {
     // Make sure this panel actually contains the child widget.
-    if (!children.contains(w)) {
+    if (!getChildren().contains(w)) {
       return false;
     }
 
     // Disown it.
     disown(w);
 
-    children.remove(w);
+    getChildren().remove(w);
     return true;
   }
 
@@ -48,9 +64,10 @@
    * 
    * @param w the child widget to be added
    * @param container the element within which the child will be contained
+   * @return the index at which the widget was added
    */
-  protected void add(Widget w, Element container) {
-    insert(w, container, children.size());
+  protected int add(Widget w, Element container) {
+    return insert(w, container, getChildren().size());
   }
 
   /**
@@ -68,13 +85,27 @@
    * @param w the child widget to be added
    * @param container the element within which the child will be contained
    * @param beforeIndex the index before which the widget will be added
+   * @return the index at which the widget was added
    */
-  protected void insert(Widget w, Element container, int beforeIndex) {
-    if (w.getParent() == this) {
-      return;
+  protected int insert(Widget w, Element container, int beforeIndex) {
+    if ((beforeIndex < 0) || (beforeIndex > getWidgetCount())) {
+      throw new IndexOutOfBoundsException();
     }
-
+    // Call this early to ensure that the table doesn't end up partially
+    // constructed when an exception is thrown from adopt().
+    int idx = getWidgetIndex(w);
+    if (idx == -1) {
+      w.removeFromParent();
+    } else {
+      remove(w);
+      // If the Widget's previous position was left of the desired new position
+      // shift the desired position left to reflect the removal
+      if (idx < beforeIndex) {
+        beforeIndex--;
+      }
+    }
     adopt(w, container);
-    children.insert(w, beforeIndex);
+    getChildren().insert(w, beforeIndex);
+    return beforeIndex;
   }
 }
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 d9953a3..2380b09 100644
--- a/user/src/com/google/gwt/user/client/ui/DeckPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DeckPanel.java
@@ -30,7 +30,7 @@
  * will be cleared.
  * </p>
  */
-public class DeckPanel extends ComplexPanel implements IndexedPanel {
+public class DeckPanel extends ComplexPanel {
 
   private Widget visibleWidget;
 
@@ -59,18 +59,6 @@
     return getWidgetIndex(visibleWidget);
   }
 
-  public Widget getWidget(int index) {
-    return getChildren().get(index);
-  }
-
-  public int getWidgetCount() {
-    return getChildren().size();
-  }
-
-  public int getWidgetIndex(Widget child) {
-    return getChildren().indexOf(child);
-  }
-
   /**
    * Inserts a widget before the specified index.
    * 
@@ -80,10 +68,6 @@
    *           range
    */
   public void insert(Widget w, int beforeIndex) {
-    if ((beforeIndex < 0) || (beforeIndex > getWidgetCount())) {
-      throw new IndexOutOfBoundsException();
-    }
-
     super.insert(w, getElement(), beforeIndex);
 
     Element child = w.getElement();
@@ -92,10 +76,6 @@
     w.setVisible(false);
   }
 
-  public boolean remove(int index) {
-    return remove(getWidget(index));
-  }
-
   public boolean remove(Widget w) {
     if (!super.remove(w)) {
       return false;
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 6a04531..28266ce 100644
--- a/user/src/com/google/gwt/user/client/ui/FlowPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/FlowPanel.java
@@ -25,7 +25,7 @@
  * <img class='gallery' src='FlowPanel.png'/>
  * </p>
  */
-public class FlowPanel extends ComplexPanel implements IndexedPanel {
+public class FlowPanel extends ComplexPanel {
 
   /**
    * Creates an empty flow panel.
@@ -43,19 +43,15 @@
     super.add(w, getElement());
   }
 
-  public Widget getWidget(int index) {
-    return getChildren().get(index);
-  }
-
-  public int getWidgetCount() {
-    return getChildren().size();
-  }
-
-  public int getWidgetIndex(Widget child) {
-    return getChildren().indexOf(child);
-  }
-
-  public boolean remove(int index) {
-    return remove(getWidget(index));
+  /**
+   * 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) {
+    super.insert(w, getElement(), beforeIndex);
   }
 }
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 f7f99ba..c3baf5d 100644
--- a/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/HorizontalPanel.java
@@ -25,8 +25,7 @@
  * <img class='gallery' src='HorizontalPanel.png'/>
  * </p>
  */
-public class HorizontalPanel extends CellPanel implements IndexedPanel,
-    HasAlignment {
+public class HorizontalPanel extends CellPanel implements HasAlignment {
 
   private HorizontalAlignmentConstant horzAlign = ALIGN_LEFT;
   private Element tableRow;
@@ -61,18 +60,6 @@
     return vertAlign;
   }
 
-  public Widget getWidget(int index) {
-    return getChildren().get(index);
-  }
-
-  public int getWidgetCount() {
-    return getChildren().size();
-  }
-
-  public int getWidgetIndex(Widget child) {
-    return getChildren().indexOf(child);
-  }
-
   /**
    * Inserts a widget before the specified index. If the Widget is already
    * attached to the HorizontalPanel, it will be moved to the specified index.
@@ -83,34 +70,14 @@
    *           range
    */
   public void insert(Widget w, int beforeIndex) {
-    // Call this early to ensure that the table doesn't end up partially
-    // constructed when an exception is thrown from adopt().
-    int idx = getWidgetIndex(w);
-    if (idx == -1) {
-      w.removeFromParent();
-    } else {
-      remove(w);
-
-      // If the Widget's previous position was left of the desired new position
-      // shift the desired position left to reflect the removal
-      if (idx < beforeIndex) {
-        beforeIndex--;
-      }
-    }
-
     Element td = DOM.createTD();
+    beforeIndex = super.insert(w, td, beforeIndex);
     DOM.insertChild(tableRow, td, beforeIndex);
 
-    super.insert(w, td, beforeIndex);
-
     setCellHorizontalAlignment(w, horzAlign);
     setCellVerticalAlignment(w, vertAlign);
   }
 
-  public boolean remove(int index) {
-    return remove(getWidget(index));
-  }
-
   public boolean remove(Widget w) {
     if (w.getParent() != this) {
       return false;
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 c819a97..54d7e82 100644
--- a/user/src/com/google/gwt/user/client/ui/StackPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/StackPanel.java
@@ -36,7 +36,7 @@
  * {@example com.google.gwt.examples.StackPanelExample}
  * </p>
  */
-public class StackPanel extends ComplexPanel implements IndexedPanel {
+public class StackPanel extends ComplexPanel {
 
   private Element body;
   private int visibleStack = -1;
@@ -63,34 +63,7 @@
    * @param w the widget to be added
    */
   public void add(Widget w) {
-    // Call this early to ensure that the table doesn't end up partially
-    // constructed when an exception is thrown from adopt().
-    w.removeFromParent();
-
-    int index = getWidgetCount();
-
-    Element tr = DOM.createTR();
-    Element td = DOM.createTD();
-    DOM.appendChild(body, tr);
-    DOM.appendChild(tr, td);
-    setStyleName(td, "gwt-StackPanelItem", true);
-    DOM.setElementPropertyInt(td, "__index", index);
-    DOM.setElementPropertyInt(td, "__owner", hashCode());
-    DOM.setElementProperty(td, "height", "1px");
-
-    tr = DOM.createTR();
-    td = DOM.createTD();
-    DOM.appendChild(body, tr);
-    DOM.appendChild(tr, td);
-    DOM.setElementProperty(td, "height", "100%");
-    DOM.setElementProperty(td, "vAlign", "top");
-
-    super.add(w, td);
-
-    setStackVisible(index, false);
-    if (visibleStack == -1) {
-      showStack(0);
-    }
+    insert(w, getWidgetCount());
   }
 
   /**
@@ -125,16 +98,53 @@
     return visibleStack;
   }
 
-  public Widget getWidget(int index) {
-    return getChildren().get(index);
-  }
+  /**
+   * 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();
+    Element tdh = DOM.createTD();
+    DOM.appendChild(trh, tdh);
 
-  public int getWidgetCount() {
-    return getChildren().size();
-  }
+    // body
+    Element trb = DOM.createTR();
+    Element tdb = DOM.createTD();
+    DOM.appendChild(trb, tdb);
 
-  public int getWidgetIndex(Widget child) {
-    return getChildren().indexOf(child);
+    beforeIndex = super.insert(w, tdb, beforeIndex);
+    // DOM indices are 2x logical indices; 2 dom elements per stack item
+    int effectiveIndex = beforeIndex * 2;
+    // this ordering puts the body below the header
+    DOM.insertChild(body, trb, effectiveIndex);
+    DOM.insertChild(body, trh, effectiveIndex);
+
+    // header styling
+    setStyleName(tdh, "gwt-StackPanelItem", true);
+    DOM.setElementPropertyInt(tdh, "__owner", hashCode());
+    DOM.setElementProperty(tdh, "height", "1px");
+
+    // body styling
+    DOM.setElementProperty(tdb, "height", "100%");
+    DOM.setElementProperty(tdb, "vAlign", "top");
+
+    // Update indices of all elements to the right.
+    updateIndicesFrom(beforeIndex);
+
+    // Correct visible stack for new location.
+    if (visibleStack == -1) {
+      showStack(0);
+    } else {
+      setStackVisible(beforeIndex, false);
+      if (visibleStack >= beforeIndex) {
+        ++visibleStack;
+      }
+    }
   }
 
   public void onBrowserEvent(Event event) {
@@ -223,6 +233,7 @@
   }
 
   private boolean remove(Widget child, int index) {
+    // TODO: is this check really necessary?
     if (child.getParent() != this) {
       return false;
     }
@@ -241,15 +252,9 @@
     tr = DOM.getChild(body, rowIndex);
     DOM.removeChild(body, tr);
     super.remove(child);
-    int rows = getWidgetCount() * 2;
 
-    // Update all the indexes.
-    for (int i = rowIndex; i < rows; i = i + 2) {
-      Element childTR = DOM.getChild(body, i);
-      Element td = DOM.getFirstChild(childTR);
-      DOM.setElementPropertyInt(td, "__index", index);
-      ++index;
-    }
+    // Update indices of all elements to the right.
+    updateIndicesFrom(rowIndex);
 
     return true;
   }
@@ -271,4 +276,12 @@
     getWidget(index).setVisible(visible);
   }
 
+  private void updateIndicesFrom(int beforeIndex) {
+    for (int i = beforeIndex, c = getWidgetCount(); i < c; ++i) {
+      Element childTR = DOM.getChild(body, i * 2);
+      Element childTD = DOM.getFirstChild(childTR);
+      DOM.setElementPropertyInt(childTD, "__index", i);
+    }
+  }
+
 }
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 947ccf2..a92c520 100644
--- a/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/VerticalPanel.java
@@ -25,8 +25,7 @@
  * <img class='gallery' src='VerticalPanel.png'/>
  * </p>
  */
-public class VerticalPanel extends CellPanel implements IndexedPanel,
-    HasAlignment {
+public class VerticalPanel extends CellPanel implements HasAlignment {
 
   private HorizontalAlignmentConstant horzAlign = ALIGN_LEFT;
   private VerticalAlignmentConstant vertAlign = ALIGN_TOP;
@@ -57,18 +56,6 @@
     return vertAlign;
   }
 
-  public Widget getWidget(int index) {
-    return getChildren().get(index);
-  }
-
-  public int getWidgetCount() {
-    return getChildren().size();
-  }
-
-  public int getWidgetIndex(Widget child) {
-    return getChildren().indexOf(child);
-  }
-
   /**
    * Inserts a widget before the specified index. If the Widget is already
    * attached to the VerticalPanel, it will be moved to the specified index.
@@ -79,37 +66,16 @@
    *           range
    */
   public void insert(Widget w, int beforeIndex) {
-    // Call this early to ensure that the table doesn't end up partially
-    // constructed when an exception is thrown from adopt().
-    int idx = getWidgetIndex(w);
-    if (idx == -1) {
-      w.removeFromParent();
-    } else {
-      remove(w);
-
-      // If the Widget's previous position was left of the desired new position
-      // shift the desired position left to reflect the removal
-      if (idx < beforeIndex) {
-        beforeIndex--;
-      }
-    }
-
     Element tr = DOM.createTR();
     Element td = DOM.createTD();
-
-    DOM.insertChild(getBody(), tr, beforeIndex);
+    beforeIndex = super.insert(w, td, beforeIndex);
     DOM.appendChild(tr, td);
-
-    super.insert(w, td, beforeIndex);
+    DOM.insertChild(getBody(), tr, beforeIndex);
 
     setCellHorizontalAlignment(w, horzAlign);
     setCellVerticalAlignment(w, vertAlign);
   }
 
-  public boolean remove(int index) {
-    return remove(getWidget(index));
-  }
-
   public boolean remove(Widget w) {
     if (w.getParent() != this) {
       return false;