001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.user.client.ui;
017:
018: import com.google.gwt.user.client.DOM;
019: import com.google.gwt.user.client.Element;
020:
021: import java.util.Iterator;
022:
023: /**
024: * Abstract base class for panels that can contain multiple child widgets.
025: */
026: public abstract class ComplexPanel extends Panel implements
027: IndexedPanel {
028:
029: private WidgetCollection children = new WidgetCollection(this );
030:
031: public Widget getWidget(int index) {
032: return getChildren().get(index);
033: }
034:
035: public int getWidgetCount() {
036: return getChildren().size();
037: }
038:
039: public int getWidgetIndex(Widget child) {
040: return getChildren().indexOf(child);
041: }
042:
043: public Iterator<Widget> iterator() {
044: return getChildren().iterator();
045: }
046:
047: public boolean remove(int index) {
048: return remove(getWidget(index));
049: }
050:
051: @Override
052: public boolean remove(Widget w) {
053: // Validate.
054: if (w.getParent() != this ) {
055: return false;
056: }
057: // Orphan.
058: orphan(w);
059:
060: // Physical detach.
061: Element elem = w.getElement();
062: DOM.removeChild(DOM.getParent(elem), elem);
063:
064: // Logical detach.
065: getChildren().remove(w);
066: return true;
067: }
068:
069: /**
070: * Adds a new child widget to the panel, attaching its Element to the
071: * specified container Element.
072: *
073: * @param child the child widget to be added
074: * @param container the element within which the child will be contained
075: */
076: protected void add(Widget child, Element container) {
077: // Detach new child.
078: child.removeFromParent();
079:
080: // Logical attach.
081: getChildren().add(child);
082:
083: // Physical attach.
084: DOM.appendChild(container, child.getElement());
085:
086: // Adopt.
087: adopt(child);
088: }
089:
090: /**
091: * Adjusts beforeIndex to account for the possibility that the given widget is
092: * already a child of this panel.
093: *
094: * @param child the widget that might be an existing child
095: * @param beforeIndex the index at which it will be added to this panel
096: * @return the modified index
097: */
098: protected int adjustIndex(Widget child, int beforeIndex) {
099: checkIndexBoundsForInsertion(beforeIndex);
100:
101: // Check to see if this widget is already a direct child.
102: if (child.getParent() == this ) {
103: // If the Widget's previous position was left of the desired new position
104: // shift the desired position left to reflect the removal
105: int idx = getWidgetIndex(child);
106: if (idx < beforeIndex) {
107: beforeIndex--;
108: }
109: }
110:
111: return beforeIndex;
112: }
113:
114: /**
115: * Checks that <code>index</code> is in the range [0, getWidgetCount()),
116: * which is the valid range on accessible indexes.
117: *
118: * @param index the index being accessed
119: */
120: protected void checkIndexBoundsForAccess(int index) {
121: if (index < 0 || index >= getWidgetCount()) {
122: throw new IndexOutOfBoundsException();
123: }
124: }
125:
126: /**
127: * Checks that <code>index</code> is in the range [0, getWidgetCount()],
128: * which is the valid range for indexes on an insertion.
129: *
130: * @param index the index where insertion will occur
131: */
132: protected void checkIndexBoundsForInsertion(int index) {
133: if (index < 0 || index > getWidgetCount()) {
134: throw new IndexOutOfBoundsException();
135: }
136: }
137:
138: /**
139: * Gets the list of children contained in this panel.
140: *
141: * @return a collection of child widgets
142: */
143: protected WidgetCollection getChildren() {
144: return children;
145: }
146:
147: /**
148: * This method was used by subclasses to insert a new child Widget. It is now
149: * deprecated because it was ambiguous whether the <code>child</code> should
150: * be appended to <code>container</code> element versus inserted into
151: * <code>container</code> at <code>beforeIndex</code>. Use
152: * {@link #insert(Widget, Element, int, boolean)}, which clarifies this
153: * ambiguity.
154: *
155: * @deprecated Use {@link #insert(Widget, Element, int, boolean)}.
156: */
157: @Deprecated
158: protected void insert(Widget child, Element container,
159: int beforeIndex) {
160: if (container == null) {
161: throw new NullPointerException("container may not be null");
162: }
163: insert(child, container, beforeIndex, false);
164: }
165:
166: /**
167: * Insert a new child Widget into this Panel at a specified index, attaching
168: * its Element to the specified container Element. The child Element will
169: * either be attached to the container at the same index, or simply appended
170: * to the container, depending on the value of <code>domInsert</code>.
171: *
172: * @param child the child Widget to be added
173: * @param container the Element within which <code>child</code> will be
174: * contained
175: * @param beforeIndex the index before which <code>child</code> will be
176: * inserted
177: * @param domInsert if <code>true</code>, insert <code>child</code> into
178: * <code>container</code> at <code>beforeIndex</code>; otherwise
179: * append <code>child</code> to the end of <code>container</code>.
180: */
181: protected void insert(Widget child, Element container,
182: int beforeIndex, boolean domInsert) {
183: // Validate index; adjust if the widget is already a child of this panel.
184: beforeIndex = adjustIndex(child, beforeIndex);
185:
186: // Detach new child.
187: child.removeFromParent();
188:
189: // Logical attach.
190: getChildren().insert(child, beforeIndex);
191:
192: // Physical attach.
193: if (domInsert) {
194: DOM.insertChild(container, child.getElement(), beforeIndex);
195: } else {
196: DOM.appendChild(container, child.getElement());
197: }
198:
199: // Adopt.
200: adopt(child);
201: }
202: }
|