com.extjs.gxt.ui.client.widget.Container Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gxt Show documentation
Show all versions of gxt Show documentation
Rich Internet Application Framework for GWT
/*
* Sencha GXT 2.3.1 - Sencha for GWT
* Copyright(c) 2007-2013, Sencha, Inc.
* [email protected]
*
* http://www.sencha.com/products/gxt/license/
*/
package com.extjs.gxt.ui.client.widget;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.ContainerEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.LayoutEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.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;
/**
* Class for any {@link BoxComponent} that can contain other components.
* Containers handle the basic behavior of containing components, namely
* managing, attaching, and detaching the child widgets.
*
*
* When children are added to a container they are not physically added to the
* DOM of the container. Subclasses are responsible for connecting the child
* components.
*
*
* Container does not define a root element. setElement must be called by any
* subclass to ensure the container has an element.
*
*
* - Events:
*
* - BeforeAdd : ContainerEvent(container, item, index)
* Fires before a item is added or inserted. Listeners can cancel the
* action by calling {@link BaseEvent#setCancelled(boolean)}.
*
* - container : this
* - item : the component being added
* - index : the index at which the component will be added
*
*
*
* - BeforeRemove : ContainerEvent(container, item)
* Fires before a item is removed. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.
*
* - container : this
* - item : the component being removed
*
*
*
* - Add : ContainerEvent(container, item, index)
* Fires after a item has been added or inserted.
*
* - container : this
* - item : the item that was added
* - index : the index at which the item will be added
*
*
*
* - Remove : ContainerEvent(container, item)
* Fires after a item has been removed.
*
* - container : this
* - item : the item being removed
*
*
*
*
*
* - Inherited Events:
* - BoxComponent Move
* - BoxComponent Resize
* - Component Enable
* - Component Disable
* - Component BeforeHide
* - Component Hide
* - Component BeforeShow
* - Component Show
* - Component Attach
* - Component Detach
* - Component BeforeRender
* - Component Render
* - Component BrowserEvent
* - Component BeforeStateRestore
* - Component StateRestore
* - Component BeforeStateSave
* - Component SaveState
*
*
* @param the child component type
*/
public abstract class Container extends BoxComponent {
/**
* True to attach the container's children (defaults to true).
*/
protected boolean attachChildren = true;
/**
* False to disable the container's layout, stopping it from executing
* (defaults to false).
*/
protected boolean enableLayout;
/**
* True to execute the container's layout when children are inserted and
* removed (defaults to false).
*/
protected boolean layoutOnChange;
protected boolean layoutExecuted;
protected boolean layoutNeeded = true;
protected boolean layoutOnAttach = true;
Listener layoutListener;
private List items;
private Layout layout;
/**
* Creates a new container.
*/
public Container() {
items = new ArrayList();
}
@Override
public void disable() {
super.disable();
for (Component c : items) {
c.disable();
}
}
@Override
public void enable() {
super.enable();
for (Component c : items) {
c.enable();
}
}
/**
* Returns the component whose element, or child element, matches the given
* element.
*
* @param elem the element
* @return the matching component or null
if no match
*/
public T findItem(Element elem) {
for (T c : items) {
if (DOM.isOrHasChild(c.getElement(), elem)) {
return c;
}
}
return null;
}
/**
* Returns the item at the given index or null if index out of bounds.
*
* @param index the index
* @return the item
*/
public T getItem(int index) {
return index < items.size() ? items.get(index) : null;
}
/**
* Returns the item with the specified item id.
*
* @param itemId the item id
* @return the item or null
if no match
*/
public T getItemByItemId(String itemId) {
for (T item : getItems()) {
if (item.getItemId().equals(itemId)) {
return item;
}
}
return null;
}
/**
* Returns the number of children.
*
* @return the component count
*/
public int getItemCount() {
return items.size();
}
/**
* Returns the child items.
*
* @return the children
*/
public List getItems() {
return items;
}
/**
* Returns the container's layout target. Only applies to container's with
* layouts.
*
* @return the layout target
*/
public El getLayoutTarget() {
return el();
}
/**
* Returns the widget at the given index. If the child is a WidgetComponent,
* the wrapped widget is returned.
*
* @param index the index
* @return the widget
*/
public Widget getWidget(int index) {
Component c = getItem(index);
if (c != null && c instanceof WidgetComponent) {
return ((WidgetComponent) c).getWidget();
}
return c;
}
/**
* Returns the index of the item.
*
* @param item the item
* @return the index
*/
public int indexOf(T item) {
return items.indexOf(item);
}
/**
* Returns an iterator over the container's children.
*
* @return an iterator
*/
public Iterator iterator() {
return items.iterator();
}
/**
* Removes all the container's items.
*
* @return true if all items where removed
*/
public boolean removeAll() {
return removeAll(false);
}
/**
* Scrolls the item into view.
*
* @param item the item
*/
public void scrollIntoView(T item) {
if (rendered && item.isRendered()) {
item.el().scrollIntoView(el().dom, false);
}
}
/**
* Adds a item to the container. Fires the BeforeAdd event before
* inserting, then fires the Add event after the widget has been
* inserted.
*
* @param item the item to be added
*/
protected boolean add(T item) {
return insert(item, getItemCount());
}
protected int adjustIndex(T child, int beforeIndex) {
int idx = indexOf(child);
if (idx != -1) {
if (idx < beforeIndex) {
beforeIndex--;
}
}
return beforeIndex;
}
/**
* Sets the child's parent to this container. In order to support lazy
* rendering this method uses JSNI to simply set the child parent without
* effecting the attached state of the child.
*
* @param child the child widget
*/
protected void adopt(T child) {
child.setParent(this);
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected ContainerEvent createContainerEvent(T item) {
return new ContainerEvent(this, item);
}
@Override
protected void doAttachChildren() {
super.doAttachChildren();
attachChildren();
}
@Override
protected void doDetachChildren() {
super.doDetachChildren();
if (attachChildren) {
for (T item : items) {
if (item.isRendered()) {
ComponentHelper.doDetach(item);
}
}
}
}
protected boolean doLayout() {
return doLayout(false);
}
protected boolean doLayout(boolean force) {
if (!enableLayout || (!force && !fireEvent(Events.BeforeLayout, createContainerEvent(null)))) {
return false;
}
if (layout == null) {
setLayout(new FlowLayout());
}
for (Component c : items) {
if (c instanceof ContentPanel) {
((ContentPanel) c).layoutBars();
}
}
// execute the layout
if (force || layoutNeeded) {
layout.layout();
}
for (Component c : items) {
if (c instanceof Composite) {
c = ((Composite) c).getComponent();
}
if (c instanceof LayoutContainer) {
((LayoutContainer) c).layout(force);
} else if (c instanceof Container>) {
Container> con = (Container>) c;
if (con.layout != null) {
con.layout(force);
}
} else {
c.recalculate();
}
}
onAfterLayout();
fireEvent(Events.AfterLayout, createContainerEvent(null));
return true;
}
protected Layout getLayout() {
return layout;
}
/**
* Adds a item into the container. Fires the BeforeAdd event before
* inserting, then fires the Add event after the widget has been
* inserted.
*
* @param item the item to insert
* @param index the insert location
*/
protected boolean insert(T item, int index) {
ContainerEvent, ?> containerEvent = createContainerEvent(item);
containerEvent.setIndex(index);
if (fireEvent(Events.BeforeAdd, containerEvent)) {
ComponentEvent componentEvent = item.createComponentEvent(null);
if (item.fireEvent(Events.BeforeAdopt, componentEvent)) {
index = adjustIndex(item, index);
item.removeFromParent();
if (item.isRendered()) {
// make sure to detach it from the dom first
item.el().remove();
}
items.add(index, item);
onInsert(item, index);
adopt(item);
item.fireEvent(Events.Adopt, componentEvent);
fireEvent(Events.Add, containerEvent);
layoutNeeded = true;
if (isRendered() && layoutOnChange) {
layout();
}
return true;
}
}
return false;
}
protected boolean isLayoutNeeded() {
return layoutNeeded;
}
/**
* Executes the container's layout. If a layout has not been set a
* FlowLayout
will be used.
*
* @return true if layout was executed.
*/
protected boolean layout() {
return layout(false);
}
/**
* * Executes the container's layout. If a layout has not been set a
* FlowLayout
will be used.
*
* @param force true to force the layout call, also if caching things it is
* not needed
* @return true if layout was executed.
*/
protected boolean layout(boolean force) {
if (!rendered) {
layoutOnAttach = true;
return false;
}
return doLayout(force);
}
protected void notifyHide() {
super.notifyHide();
for (Component c : items) {
if (!c.hidden && c.isRendered()) {
c.notifyHide();
}
}
}
protected void notifyShow() {
super.notifyShow();
for (Component c : items) {
if (!c.hidden && c.isRendered()) {
c.notifyShow();
}
}
}
protected void onAfterLayout() {
}
@Override
protected void onAttach() {
super.onAttach();
if (!layoutExecuted && layoutOnAttach) {
boolean parentIsContainer = getParent() != null && getParent() instanceof Container>;
if (parentIsContainer) {
Container> parent = (Container>) getParent();
if (parent.getLayout() == null || getLayout() == null || !getLayout().monitorResize || !getLayout().isRunning()) {
layout();
}
} else {
layout();
}
}
}
protected void onBeforeLayoutExcecuted(Layout layout) {
}
protected void onInsert(T item, int index) {
if (isAutoHeight() || isAutoWidth()) {
sync(true);
}
}
protected void onLayoutExcecuted(Layout layout) {
layoutExecuted = true;
layoutNeeded = false;
attachChildren();
sync(true);
}
protected void onRemove(T item) {
if (isAutoHeight() || isAutoWidth()) {
sync(true);
}
}
protected final void orphan(T child) {
assert (child.getParent() == this);
if (child.isAttached()) {
ComponentHelper.doDetach(child);
assert !child.isAttached() : "Failure of " + getClass() + " to call super.onDetach()";
}
child.setParent(null);
}
/**
* Removes the item from the container. Fires the BeforeRemove event
* before removing, then fires the Remove event after the widget has
* been removed.
*
* @param item the item to remove
* @return true
if the item was removed
*/
protected boolean remove(T item) {
return remove(item, false);
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected boolean remove(T component, boolean force) {
ContainerEvent containerEvent = createContainerEvent(component);
containerEvent.setItem(component);
containerEvent.setIndex(indexOf(component));
if (fireEvent(Events.BeforeRemove, containerEvent) || force) {
ComponentEvent componentEvent = component.createComponentEvent(null);
if (component.fireEvent(Events.BeforeOrphan, componentEvent) || force) {
onRemove(component);
if (attachChildren) {
assert component.getParent() == this :"component is not a child of this container";
orphan(component);
}
if (rendered) {
Element elem = component.getElement();
Element parent = DOM.getParent(elem);
if (parent != null) {
DOM.removeChild(parent, elem);
}
}
items.remove(component);
component.fireEvent(Events.Orphan, componentEvent);
fireEvent(Events.Remove, containerEvent);
layoutNeeded = true;
if (rendered && layoutOnChange) {
layout();
}
return true;
}
}
return false;
}
protected boolean removeAll(boolean force) {
int count = getItemCount();
for (int i = 0; i < count; i++) {
remove(getItem(0), force);
}
return getItemCount() == 0;
}
/**
* Sets the container's layout.
*
* @param layout the new layout
*/
protected void setLayout(Layout layout) {
if (layoutListener == null) {
layoutListener = new Listener() {
public void handleEvent(LayoutEvent be) {
if (be.getType() == Events.BeforeLayout) {
onBeforeLayoutExcecuted(be.getLayout());
} else if (be.getType() == Events.AfterLayout) {
onLayoutExcecuted(be.getLayout());
}
}
};
}
if (this.layout != null) {
this.layout.removeListener(Events.BeforeLayout, layoutListener);
this.layout.removeListener(Events.AfterLayout, layoutListener);
this.layout.setContainer(null);
}
this.layout = layout;
this.layout.addListener(Events.BeforeLayout, layoutListener);
this.layout.addListener(Events.AfterLayout, layoutListener);
layoutNeeded = true;
layout.setContainer(this);
}
protected void setLayoutNeeded(boolean layoutNeeded) {
this.layoutNeeded = layoutNeeded;
}
protected void setLayoutOnChange(boolean change) {
this.layoutOnChange = change;
}
/**
* Helper Method for the subclasses that wish to support automatic wrapping of
* Widget instances in WidgetComponents
*
*
* If the widget is a component, no wrapping is performed
*
* @param widget the widget to be wrapped
* @return the new component
*/
protected Component wrapWidget(Widget widget) {
if (widget instanceof Component) {
return (Component) widget;
} else {
return new WidgetComponent(widget);
}
}
private void attachChildren() {
if (attachChildren && isAttached()) {
for (T item : items) {
if (item.isRendered()) {
ComponentHelper.doAttach(item);
}
}
}
}
}