| /* |
| * Copyright 2007 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.user.client.ui; |
| |
| import com.google.gwt.user.client.DOM; |
| import com.google.gwt.user.client.Element; |
| |
| import java.util.Iterator; |
| |
| /** |
| * Abstract base class for panels that can contain multiple child widgets. |
| */ |
| public abstract class ComplexPanel extends Panel implements IndexedPanel.ForIsWidget { |
| |
| private WidgetCollection children = new WidgetCollection(this); |
| |
| /** |
| * The command used to orphan children. |
| */ |
| private AttachDetachException.Command orphanCommand; |
| |
| 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 int getWidgetIndex(IsWidget child) { |
| return getWidgetIndex(asWidgetOrNull(child)); |
| } |
| |
| public Iterator<Widget> iterator() { |
| return getChildren().iterator(); |
| } |
| |
| public boolean remove(int index) { |
| return remove(getWidget(index)); |
| } |
| |
| @Override |
| public boolean remove(Widget w) { |
| // Validate. |
| if (w.getParent() != this) { |
| return false; |
| } |
| // Orphan. |
| try { |
| orphan(w); |
| } finally { |
| // Physical detach. |
| Element elem = w.getElement(); |
| DOM.removeChild(DOM.getParent(elem), elem); |
| |
| // Logical detach. |
| getChildren().remove(w); |
| } |
| return true; |
| } |
| |
| /** |
| * Adds a new child widget to the panel, attaching its Element to the |
| * specified container Element. |
| * |
| * @param child the child widget to be added |
| * @param container the element within which the child will be contained |
| */ |
| protected void add(Widget child, Element container) { |
| // Detach new child. |
| child.removeFromParent(); |
| |
| // Logical attach. |
| getChildren().add(child); |
| |
| // Physical attach. |
| DOM.appendChild(container, child.getElement()); |
| |
| // Adopt. |
| adopt(child); |
| } |
| |
| /** |
| * Adjusts beforeIndex to account for the possibility that the given widget is |
| * already a child of this panel. |
| * |
| * @param child the widget that might be an existing child |
| * @param beforeIndex the index at which it will be added to this panel |
| * @return the modified index |
| */ |
| protected int adjustIndex(Widget child, int beforeIndex) { |
| checkIndexBoundsForInsertion(beforeIndex); |
| |
| // Check to see if this widget is already a direct child. |
| if (child.getParent() == this) { |
| // If the Widget's previous position was left of the desired new position |
| // shift the desired position left to reflect the removal |
| int idx = getWidgetIndex(child); |
| if (idx < beforeIndex) { |
| beforeIndex--; |
| } |
| } |
| |
| return beforeIndex; |
| } |
| |
| /** |
| * Checks that <code>index</code> is in the range [0, getWidgetCount()), which |
| * is the valid range on accessible indexes. |
| * |
| * @param index the index being accessed |
| */ |
| protected void checkIndexBoundsForAccess(int index) { |
| if (index < 0 || index >= getWidgetCount()) { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * Checks that <code>index</code> is in the range [0, getWidgetCount()], which |
| * is the valid range for indexes on an insertion. |
| * |
| * @param index the index where insertion will occur |
| */ |
| protected void checkIndexBoundsForInsertion(int index) { |
| if (index < 0 || index > getWidgetCount()) { |
| throw new IndexOutOfBoundsException(); |
| } |
| } |
| |
| /** |
| * Gets the list of children contained in this panel. |
| * |
| * @return a collection of child widgets |
| */ |
| protected WidgetCollection getChildren() { |
| return children; |
| } |
| |
| /** |
| * This method was used by subclasses to insert a new child Widget. It is now |
| * deprecated because it was ambiguous whether the <code>child</code> should |
| * be appended to <code>container</code> element versus inserted into |
| * <code>container</code> at <code>beforeIndex</code>. Use |
| * {@link #insert(Widget, Element, int, boolean)}, which clarifies this |
| * ambiguity. |
| * |
| * @deprecated Use {@link #insert(Widget, Element, int, boolean)} instead |
| */ |
| @Deprecated |
| protected void insert(Widget child, Element container, int beforeIndex) { |
| if (container == null) { |
| throw new NullPointerException("container may not be null"); |
| } |
| insert(child, container, beforeIndex, false); |
| } |
| |
| /** |
| * Insert a new child Widget into this Panel at a specified index, attaching |
| * its Element to the specified container Element. The child Element will |
| * either be attached to the container at the same index, or simply appended |
| * to the container, depending on the value of <code>domInsert</code>. |
| * |
| * @param child the child Widget to be added |
| * @param container the Element within which <code>child</code> will be |
| * contained |
| * @param beforeIndex the index before which <code>child</code> will be |
| * inserted |
| * @param domInsert if <code>true</code>, insert <code>child</code> into |
| * <code>container</code> at <code>beforeIndex</code>; otherwise |
| * append <code>child</code> to the end of <code>container</code>. |
| */ |
| protected void insert(Widget child, Element container, int beforeIndex, |
| boolean domInsert) { |
| // Validate index; adjust if the widget is already a child of this panel. |
| beforeIndex = adjustIndex(child, beforeIndex); |
| |
| // Detach new child. |
| child.removeFromParent(); |
| |
| // Logical attach. |
| getChildren().insert(child, beforeIndex); |
| |
| // Physical attach. |
| if (domInsert) { |
| DOM.insertChild(container, child.getElement(), beforeIndex); |
| } else { |
| DOM.appendChild(container, child.getElement()); |
| } |
| |
| // Adopt. |
| adopt(child); |
| } |
| |
| void doLogicalClear() { |
| // TODO(jgw): When Layout work has landed, deprecate FlowPanel (the only |
| // caller of this method in our code), and deprecate this method with an eye |
| // to making it private down the road. |
| |
| // Only use one orphan command per panel to avoid object creation. |
| if (orphanCommand == null) { |
| orphanCommand = new AttachDetachException.Command() { |
| public void execute(Widget w) { |
| orphan(w); |
| } |
| }; |
| } |
| try { |
| AttachDetachException.tryCommand(this, orphanCommand); |
| } finally { |
| children = new WidgetCollection(this); |
| } |
| } |
| } |