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 all panels, which are widgets that can contain other
025: * widgets.
026: */
027: public abstract class Panel extends Widget implements HasWidgets {
028:
029: /**
030: * Adds a child widget.
031: *
032: * <p>
033: * <b>How to Override this Method</b>
034: * </p>
035: * <p>
036: * There are several important things that must take place in the correct
037: * order to properly add or insert a Widget to a Panel. Not all of these steps
038: * will be relevant to every Panel, but all of the steps must be considered.
039: * <ol>
040: * <li><b>Validate:</b> Perform any sanity checks to ensure the Panel can
041: * accept a new Widget. Examples: checking for a valid index on insertion;
042: * checking that the Panel is not full if there is a max capacity.</li>
043: * <li><b>Adjust for Reinsertion:</b> Some Panels need to handle the case
044: * where the Widget is already a child of this Panel. Example: when performing
045: * a reinsert, the index might need to be adjusted to account for the Widget's
046: * removal. See {@link ComplexPanel#adjustIndex(Widget, int)}.</li>
047: * <li><b>Detach Child:</b> Remove the Widget from its existing parent, if
048: * any. Most Panels will simply call {@link Widget#removeFromParent()} on the
049: * Widget.</li>
050: * <li><b>Logical Attach:</b> Any state variables of the Panel should be
051: * updated to reflect the addition of the new Widget. Example: the Widget is
052: * added to the Panel's {@link WidgetCollection} at the appropriate index.</li>
053: * <li><b>Physical Attach:</b> The Widget's Element must be physically
054: * attached to the Panel's Element, either directly or indirectly.</li>
055: * <li><b>Adopt:</b> Call {@link #adopt(Widget)} to finalize the add as the
056: * very last step.</li>
057: * </ol>
058: * </p>
059: *
060: * @param child the widget to be added
061: * @throws UnsupportedOperationException if this method is not supported (most
062: * often this means that a specific overload must be called)
063: * @see HasWidgets#add(Widget)
064: */
065: public void add(Widget child) {
066: throw new UnsupportedOperationException(
067: "This panel does not support no-arg add()");
068: }
069:
070: public void clear() {
071: Iterator<Widget> it = iterator();
072: while (it.hasNext()) {
073: it.next();
074: it.remove();
075: }
076: }
077:
078: /**
079: * Removes a child widget.
080: *
081: * <p>
082: * <b>How to Override this Method</b>
083: * </p>
084: * <p>
085: * There are several important things that must take place in the correct
086: * order to properly remove a Widget from a Panel. Not all of these steps will
087: * be relevant to every Panel, but all of the steps must be considered.
088: * <ol>
089: * <li><b>Validate:</b> Make sure this Panel is actually the parent of the
090: * child Widget; return <code>false</code> if it is not.</li>
091: * <li><b>Orphan:</b> Call {@link #orphan(Widget)} first while the child
092: * Widget is still attached.</li>
093: * <li><b>Physical Detach:</b> Adjust the DOM to account for the removal of
094: * the child Widget. The Widget's Element must be physically removed from the
095: * DOM.</li>
096: * <li><b>Logical Detach:</b> Update the Panel's state variables to reflect
097: * the removal of the child Widget. Example: the Widget is removed from the
098: * Panel's {@link WidgetCollection}.</li>
099: * </ol>
100: * </p>
101: *
102: * @param child the widget to be removed
103: * @return <code>true</code> if the child was present
104: */
105: public abstract boolean remove(Widget child);
106:
107: /**
108: * Finalize the attachment of a Widget to this Panel. This method is the
109: * <b>last</b> step in adding or inserting a Widget into a Panel, and should
110: * be called after physical attachment in the DOM is complete. This Panel
111: * becomes the parent of the child Widget, and the child will now fire its
112: * {@link Widget#onAttach()} event if this Panel is currently attached.
113: *
114: * @param child the widget to be adopted
115: * @see #add(Widget)
116: */
117: protected final void adopt(Widget child) {
118: assert (child.getParent() == null);
119: child.setParent(this );
120: }
121:
122: /**
123: * This method was formerly part of the process of adding a Widget to a Panel
124: * but has been deprecated in favor of {@link #adopt(Widget)}.
125: *
126: * @deprecated Use {@link #adopt(Widget)}.
127: */
128: @Deprecated
129: protected void adopt(Widget w, Element container) {
130: // Remove the widget from its current parent, if any.
131: w.removeFromParent();
132:
133: // Attach it at the DOM and GWT levels.
134: if (container != null) {
135: DOM.appendChild(container, w.getElement());
136: }
137: w.setParent(this );
138: }
139:
140: /**
141: * This method was formerly part of the process of removing a Widget from a
142: * Panel but has been deprecated in favor of {@link #orphan(Widget)}.
143: *
144: * @deprecated Use {@link #orphan(Widget)}.
145: */
146: @Deprecated
147: protected void disown(Widget w) {
148: // Only disown it if it's actually contained in this panel.
149: if (w.getParent() != this ) {
150: throw new IllegalArgumentException(
151: "w is not a child of this panel");
152: }
153:
154: // setParent() must be called before removeChild() to ensure that the
155: // element is still attached when onDetach()/onUnload() are called.
156: Element elem = w.getElement();
157: w.setParent(null);
158: DOM.removeChild(DOM.getParent(elem), elem);
159: }
160:
161: @Override
162: protected void doAttachChildren() {
163: // Ensure that all child widgets are attached.
164: for (Iterator<Widget> it = iterator(); it.hasNext();) {
165: Widget child = it.next();
166: child.onAttach();
167: }
168: }
169:
170: @Override
171: protected void doDetachChildren() {
172: // Ensure that all child widgets are detached.
173: for (Iterator<Widget> it = iterator(); it.hasNext();) {
174: Widget child = it.next();
175: child.onDetach();
176: }
177: }
178:
179: /**
180: * A Panel's onLoad method will be called after all of its children are
181: * attached.
182: *
183: * @see Widget#onLoad()
184: */
185: @Override
186: protected void onLoad() {
187: }
188:
189: /**
190: * A Panel's onUnload method will be called before its children become
191: * detached themselves.
192: *
193: * @see Widget#onLoad()
194: */
195: @Override
196: protected void onUnload() {
197: }
198:
199: /**
200: * This method must be called as part of the remove method of any Panel. It
201: * ensures that the Widget's parent is cleared. This method should be called
202: * after verifying that the child Widget is an existing child of the Panel,
203: * but before physically removing the child Widget from the DOM. The child
204: * will now fire its {@link Widget#onDetach()} event if this Panel is
205: * currently attached.
206: *
207: * @param child the widget to be disowned
208: * @see #add(Widget)
209: */
210: protected final void orphan(Widget child) {
211: assert (child.getParent() == this);
212: child.setParent(null);
213: }
214: }
|