/*
* MyGWT Widget Library
* Copyright(c) 2007, MyGWT.
* licensing@mygwt.net
*
* http://mygwt.net/license
*/
package net.mygwt.ui.client.widget;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.mygwt.ui.client.MyDOM;
import net.mygwt.ui.client.util.Size;
import net.mygwt.ui.client.widget.layout.FlowLayout;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.WidgetHelper;
/**
* A panel which lays out it's children using a layout manager.
*
* <dl>
* <dt><b>Events:</b></dt>
*
* <dd><b>BeforeAdd</b> : (widget, item, index)<br>
* <div>Fires before a widget is added or inserted. Listeners can set the
* <code>doit</code> field to <code>false</code> to cancel the action.</div>
* <ul>
* <li>widget : this</li>
* <li>item : the widget being added</li>
* <li>index : the index at which the widget will be added</li>
* </ul>
* </dd>
*
* <dd><b>BeforeRemove</b> : (widget, item)<br>
* <div>Fires before a widget is removed. Listeners can set the
* <code>doit</code> field to <code>false</code> to cancel the action.</div>
* <ul>
* <li>widget : this</li>
* <li>item : the widget being removed</li>
* </ul>
* </dd>
*
* <dd><b>Add</b> : (widget, item, index)<br>
* <div>Fires after a widget has been added or inserted.</div>
* <ul>
* <li>widget : this</li>
* <li>item : the widget that was added</li>
* <li>index : the index at which the item will be added</li>
* </ul>
* </dd>
*
* <dd><b>Remove</b> : (widget, item)<br>
* <div>Fires after a widget has been removed.</div>
* <ul>
* <li>widget : this</li>
* <li>item : the widget being removed</li>
* </ul>
* </dd>
*
* <dt><b>CSS:</b></dt>
* <dd>.my-container (the container itself)</dd>
* </dl>
*
* @see Layout
*/
public abstract class Container extends Component {
/**
* <code>true</code> to automatically layout container when it is resized.
* Default value is <code>true</code>.
*/
public boolean monitorResize = true;
/**
* attachChildren specifies if the container's children should be attached and
* detached. Default value is <code>true</code>.
*/
public boolean attachChildren = true;
/**
* disableLayout specifies if the container's layout is disabled. Default
* value is <code>false</code>.
*/
public boolean disableLayout = false;
protected Size lastSize;
protected List items;
protected Layout layout;
private Map layoutMap;
/**
* Creates a new container.
*/
public Container() {
items = new ArrayList();
}
/**
* Removes the layout data for the specified widget.
*
* @param widget the widget
*/
public void removeLayoutData(Widget widget) {
if (layoutMap != null) {
layoutMap.remove(widget);
}
}
/**
* Returns the widget's layout data.
*
* @param widget the widget
* @return the layout data
*/
public Object getLayoutData(Widget widget) {
if (layoutMap == null) {
return null;
}
return layoutMap.get(widget);
}
/**
* Sets the layout data for the widget.
*
* @param widget the widget
* @param layoutData the widget's layout data
*/
public void setLayoutData(Widget widget, Object layoutData) {
if (layoutMap == null) {
layoutMap = new HashMap();
}
layoutMap.put(widget, layoutData);
}
/**
* Removes all child widgets.
*/
public void clear() {
int size = getWidgetCount();
for (int i = 0; i < size; i++) {
Widget w = getWidget(0);
remove(w);
}
}
/**
* Returns the layout which is associated with the container, or
* <code>null</code> if one has not been set.
*
* @return the container's layout or <code>null</code>
*/
public Layout getLayout() {
return layout;
}
/**
* Override this method to specify the element the container's children will
* be inserted.
*
* @return the element to be used as the panel's container
*/
public Element getLayoutTarget() {
return getElement();
}
/**
* Returns the widget at the specified index.
*
* @param index the index
* @return the widget
*/
public Widget getWidget(int index) {
return (Widget) items.get(index);
}
/**
* Returns the number of child widgets.
*
* @return the count
*/
public int getWidgetCount() {
return items.size();
}
/**
* Returns an iterator over the container's child widgets.
*
* @return an iterator
*/
public Iterator iterator() {
return items.iterator();
}
/**
* Asks the layout to lay out the container's children. If a layout has not
* been set a <code>FlowLayout</code> will be used. If the size of the
* container has not changed sinse the last time layout was called it will not
* be execute. See {@link #layout(boolean)} to force the layout to execute.
*/
public void layout() {
if (disableLayout) {
return;
}
if (layout == null) {
layout = new FlowLayout();
}
onLayout();
}
/**
* Asks the layout to lay out the container's children. If a layout has not
* been set a <code>FlowLayout</code> will be used.
*
* @param force <code>true</code> to force the layout to execute
*/
public void layout(boolean force) {
if (force) {
lastSize = null;
}
layout();
}
/**
* Sets the container's layout.
*
* @param layout the new layout
*/
public void setLayout(Layout layout) {
this.layout = layout;
}
protected final void adopt(Widget child) {
assert (child.getParent() == null);
WidgetHelper.setParent(child, this);
}
protected void doAttachChildren() {
if (attachChildren) {
for (Iterator it = items.iterator(); it.hasNext();) {
Widget child = (Widget) it.next();
WidgetHelper.doAttach(child);
}
}
}
protected void doDetachChildren() {
if (attachChildren) {
for (Iterator it = items.iterator(); it.hasNext();) {
Widget child = (Widget) it.next();
WidgetHelper.doDetach(child);
}
}
}
protected void onLayout() {
if (!isAttached()) {
return;
}
if (getWidgetCount() > 0) {
Size size = MyDOM.getSize(getLayoutTarget());
int width = size.width;
int height = size.height;
if (lastSize != null) {
if (lastSize.width == width && lastSize.height == height) {
return;
}
}
lastSize = new Size(width, height);
}
layout.layout(this);
}
protected void onResize(int width, int height) {
if (monitorResize) {
layout();
}
}
protected final void orphan(Widget child) {
assert (child.getParent() == this);
WidgetHelper.setParent(child, null);
}
protected void onLoad() {
super.onLoad();
layout();
}
/**
* Removes a widget from the container.
*
* @param w the widget to remove
* @return <code>true</code> if the widget was removed
*/
public boolean remove(Widget w) {
if (attachChildren) {
if (w.getParent() != this) {
return false;
}
// Orphan.
orphan(w);
}
if (rendered) {
Element elem = w.getElement();
Element parent = DOM.getParent(elem);
if (parent != null) {
DOM.removeChild(parent, elem);
}
}
// Logical detach.
items.remove(w);
removeLayoutData(w);
return true;
}
}