001: /*
002: * GWT-Ext Widget Library
003: * Copyright(c) 2007-2008, GWT-Ext.
004: * licensing@gwt-ext.com
005: *
006: * http://www.gwt-ext.com/license
007: */
008: package com.gwtext.client.widgets;
009:
010: import com.google.gwt.core.client.JavaScriptObject;
011: import com.google.gwt.user.client.Element;
012: import com.google.gwt.user.client.ui.HasWidgets;
013: import com.google.gwt.user.client.ui.Widget;
014: import com.gwtext.client.core.Ext;
015: import com.gwtext.client.util.DOMUtil;
016: import com.gwtext.client.util.JavaScriptObjectHelper;
017: import com.gwtext.client.widgets.event.ContainerListener;
018: import com.gwtext.client.widgets.layout.CardLayout;
019: import com.gwtext.client.widgets.layout.ContainerLayout;
020: import com.gwtext.client.widgets.layout.HorizontalLayout;
021: import com.gwtext.client.widgets.layout.LayoutData;
022:
023: import java.util.ArrayList;
024: import java.util.Iterator;
025:
026: /**
027: * Base class for any {@link BoxComponent} that can contain other components. Containers handle the basic behavior of
028: * containing items, namely adding, inserting and removing them. The specific layout logic required to visually render
029: * contained items is delegated to any one of the different layout classes available. This class is intended to be extended
030: * and should generally not need to be created directly via the new keyword.
031: */
032: public class Container extends BoxComponent implements HasWidgets {
033:
034: private static JavaScriptObject configPrototype;
035: private String activeItemID;
036: private ContainerLayout layout;
037: private DefaultsHandler defaultsHandler;
038:
039: static {
040: init();
041: }
042:
043: private static native void init()/*-{
044: var c = new $wnd.Ext.Container();
045: @com.gwtext.client.widgets.Container::configPrototype = c.initialConfig;
046: }-*/;
047:
048: protected JavaScriptObject getConfigPrototype() {
049: return configPrototype;
050: }
051:
052: public String getXType() {
053: return "container";
054: }
055:
056: public Container() {
057: }
058:
059: public Container(JavaScriptObject jsObj) {
060: super (jsObj);
061: }
062:
063: public Container(Element element) {
064: super (element);
065: }
066:
067: protected native JavaScriptObject create(JavaScriptObject config) /*-{
068: return new $wnd.Ext.Container(config);
069: }-*/;
070:
071: private static Container instance(JavaScriptObject jsObj) {
072: return new Container(jsObj);
073: }
074:
075: public void setDefaults(DefaultsHandler defaultsHandler) {
076: this .defaultsHandler = defaultsHandler;
077: }
078:
079: /**
080: * Add a Component to the Container.
081: *
082: * @param component the component to add
083: * @param layoutData the layout to use when adding the component
084: */
085: public void add(Component component, LayoutData layoutData) {
086: if (defaultsHandler != null) {
087: defaultsHandler.apply(component);
088: }
089: JavaScriptObject componentJS = component.isCreated() ? component
090: .getOrCreateJsObj()
091: : component.getConfig();
092: //JavaScriptObject componentJS = component.getJsObj();
093: JavaScriptObjectHelper
094: .apply(layoutData.getJsObj(), componentJS);
095: if (layout != null && layout.getSpacing() != null) {
096: Panel panel = new Panel();
097: panel.setBaseCls("x-plain");
098: panel.setBorder(false);
099: panel.setBodyStyle(layout.getSpacing());
100: panel.add(component);
101: add(panel.getConfig());
102: } else {
103: add(componentJS);
104: }
105: }
106:
107: /**
108: * Add a widget to the Container.
109: *
110: * @param widget the widget to add
111: */
112: public void add(final Widget widget) {
113: if (widget instanceof Component) {
114: add((Component) widget);
115: } else {
116: String id = DOMUtil.getID(widget);
117: if (id == null) {
118: id = Ext.generateId();
119: DOMUtil.setID(widget, id);
120: }
121: JavaScriptObject compJS = getComponentJS(id);
122: WidgetComponent component = null;
123: if (compJS != null) {
124: component = new WidgetComponent(compJS);
125: component.setVisible(true);
126: } else {
127: component = new WidgetComponent(widget);
128: }
129: add(component);
130: }
131: }
132:
133: /**
134: * Add a widget to the Container.
135: *
136: * @param widget the widget to add
137: * @param layoutData the layout to use when adding the component
138: */
139: public void add(final Widget widget, LayoutData layoutData) {
140: if (widget instanceof Component) {
141: add((Component) widget, layoutData);
142: } else {
143: String id = DOMUtil.getID(widget);
144: if (id == null) {
145: id = Ext.generateId();
146: DOMUtil.setID(widget, id);
147: }
148: JavaScriptObject compJS = getComponentJS(id);
149: WidgetComponent component = null;
150: if (compJS != null) {
151: component = new WidgetComponent(compJS);
152: component.setVisible(true);
153: } else {
154: component = new WidgetComponent(widget);
155: }
156: add(component, layoutData);
157: }
158: }
159:
160: /**
161: * Add a Component to the Container.
162: *
163: * @param component the component to add
164: */
165: public void add(Component component) {
166:
167: if (defaultsHandler != null) {
168: defaultsHandler.apply(component);
169: }
170: JavaScriptObject componentJS = component.isCreated() ? component
171: .getOrCreateJsObj()
172: : component.getConfig();
173: if (layout != null && layout.getSpacing() != null) {
174: Panel panel = new Panel();
175: panel.setBaseCls("x-plain");
176: panel.setBorder(false);
177: panel.setBodyStyle(layout.getSpacing());
178: panel.add(component);
179: componentJS = panel.getConfig();
180: if (layout instanceof HorizontalLayout) {
181: HorizontalLayout l = (HorizontalLayout) layout;
182: l.setColumns(l.getColumns() + 1);
183: }
184: }
185: if (isCreated()) {
186: addPostCreate(componentJS);
187:
188: } else {
189: addPreCreate(componentJS);
190: }
191: }
192:
193: private void add(JavaScriptObject componentJS) {
194: if (isCreated()) {
195: addPostCreate(componentJS);
196: } else {
197: addPreCreate(componentJS);
198: }
199: }
200:
201: private native void addPreCreate(JavaScriptObject componentJS) /*-{
202: var config = this.@com.gwtext.client.widgets.Component::config;
203:
204: if(!config.items) {
205: config.items = @com.gwtext.client.util.JavaScriptObjectHelper::createJavaScriptArray()();
206: }
207: config.items.push(componentJS);
208: }-*/;
209:
210: private native void addPostCreate(JavaScriptObject componentJS) /*-{
211: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
212: container.add(componentJS);
213: }-*/;
214:
215: /**
216: * Find a component under this container at any level by id.
217: *
218: * @param id the component ID
219: * @return the component
220: */
221: public native Component findByID(String id) /*-{
222: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
223: var comp = container.findById(id);
224: return comp == null || comp === undefined ? null : @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(comp);
225: }-*/;
226:
227: /**
228: * Find a component under this container at any level by xtype.
229: *
230: * @param xtype the components xtype
231: * @return an array of components
232: */
233: public native Component[] findByType(String xtype) /*-{
234: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
235: var comps = container.findByType(xtype);
236: return comps == null || comps === undefined ? null : @com.gwtext.client.util.JavaScriptObjectHelper::convertToJavaComponentArray(Lcom/google/gwt/core/client/JavaScriptObject;)(comps);
237: }-*/;
238:
239: /**
240: * Find a component under this container at any level by a custom function. If the passed function returns true,
241: * the component will be included in the results.
242: *
243: * @param cb the find function
244: * @return an array of Components
245: */
246: public native Component[] findBy(ComponentTraversalCallback cb)/*-{
247: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
248: var containerJ = this;
249: var comps = container.findBy(function(comp) {
250: var compJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(comp);
251: return cb.@com.gwtext.client.widgets.ComponentTraversalCallback::execute(Lcom/gwtext/client/widgets/Component;)(compJ);
252: });
253: return @com.gwtext.client.util.JavaScriptObjectHelper::convertToJavaComponentArray(Lcom/google/gwt/core/client/JavaScriptObject;)(comps);
254: }-*/;
255:
256: /**
257: * Gets a direct child Component by id.
258: *
259: * @param id the component ID
260: * @return the component
261: */
262: public native Component getComponent(String id) /*-{
263: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
264: var comp = container.getComponent(id);
265: return comp == null || comp === undefined ? null : @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(comp);
266: }-*/;
267:
268: /**
269: * Gets a direct child Component by by index.
270: *
271: * @param index the component index
272: * @return the component
273: */
274: public native Component getComponent(int index) /*-{
275: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
276: var comp = container.getComponent(index);
277: return comp == null || comp === undefined ? null : @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(comp);
278: }-*/;
279:
280: /**
281: * Bubbles up the component/container heirarchy, calling the specified function with each component.
282: * If the function returns false at any point, the bubble is stopped.
283: *
284: * @param cb the traversal callback
285: */
286: public native void bubble(ComponentTraversalCallback cb)/*-{
287: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
288: var containerJ = this;
289: container.bubble(function(comp) {
290: var compJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(comp);
291: return cb.@com.gwtext.client.widgets.ComponentTraversalCallback::execute(Lcom/gwtext/client/widgets/Component;)(compJ);
292: });
293: }-*/;
294:
295: /**
296: * Cascades down the component/container heirarchy from this component (called first), calling the specified function with each component.
297: * If the function returns false at any point, the cascade is stopped on that branch.
298: *
299: * @param cb the traversal callback
300: */
301: public native void cascade(ComponentTraversalCallback cb)/*-{
302: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
303: var containerJ = this;
304: container.cascade(function(comp) {
305: var compJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(comp);
306: return cb.@com.gwtext.client.widgets.ComponentTraversalCallback::execute(Lcom/gwtext/client/widgets/Component;)(compJ);
307: });
308: }-*/;
309:
310: /**
311: * Inserts a Component into this Container at a specified index. Fires the beforeadd event before inserting, then fires
312: * the add event after the Component has been inserted.
313: *
314: * @param index the index to insert the component at
315: * @param component the component to insert
316: */
317: public void insert(int index, Component component) {
318: JavaScriptObject componentJS = component.isCreated() ? component
319: .getOrCreateJsObj()
320: : component.getConfig();
321: doInsert(index, componentJS);
322: }
323:
324: private native void doInsert(int index, JavaScriptObject component) /*-{
325: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
326: container.insert(index, component);
327: }-*/;
328:
329: /**
330: * Removes a component from this container. Fires the beforeremove event before removing, then fires the remove event after the component has been removed.
331: *
332: * @param id the id of the Component to remove
333: */
334: public native void remove(String id) /*-{
335: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
336: container.remove(id);
337: }-*/;
338:
339: public boolean remove(Widget w) {
340: String id = DOMUtil.getID(w);
341: if (getComponent(id) != null) {
342: remove(id);
343: return true;
344: } else {
345: return false;
346: }
347: }
348:
349: /**
350: * Remove all child elements destroying them.
351: */
352: public void clear() {
353: removeAll(true);
354: }
355:
356: /**
357: * The collection of components in this container. Alias for {@link #getComponents()}
358: *
359: * @return child components
360: */
361: public Component[] getItems() {
362: return getComponents();
363: }
364:
365: /**
366: * The collection of components in this container.
367: *
368: * @return child components
369: */
370: public native Component[] getComponents()/*-{
371: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
372: var items = container.items;
373: if(items === undefined || items == null) {
374: items = null;
375: } else {
376: items = container.items.items;
377: }
378: return @com.gwtext.client.util.JavaScriptObjectHelper::convertToJavaComponentArray(Lcom/google/gwt/core/client/JavaScriptObject;)(items);
379: }-*/;
380:
381: public Iterator iterator() {
382: ArrayList list = new ArrayList();
383: Component[] items = getComponents();
384: for (int i = 0; i < items.length; i++) {
385: Component item = items[i];
386: list.add(item);
387: }
388: return list.iterator();
389: }
390:
391: /**
392: * Removes a component from this container. Fires the beforeremove event before removing, then fires the remove event after the component has been removed.
393: *
394: * @param component the id of the Component to remove
395: * @param autoDestroy true to automatically invoke the Component's destroy method
396: */
397: public native void remove(Component component, boolean autoDestroy) /*-{
398: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
399: var componentJS = component.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
400: container.remove(componentJS, autoDestroy);
401: }-*/;
402:
403: /**
404: * Removes a component from this container. Fires the beforeremove event before removing, then fires the remove event after the component has been removed.
405: *
406: * @param id the id of the Component to remove
407: * @param autoDestroy true to automatically invoke the Component's destroy method
408: */
409: public native void remove(String id, boolean autoDestroy) /*-{
410: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
411: container.remove(id, autoDestroy);
412: }-*/;
413:
414: /**
415: * Removes all child components without destroying them.
416: */
417: public native void removeAll() /*-{
418: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
419: if(container.items){
420: var cs = container.items.items;
421:
422: for(var i = 0, len = cs.length; i < len; i++) {
423: cs[i].hide();
424: container.remove(cs[i], false);
425: }
426: }
427: }-*/;
428:
429: /**
430: * Removes all components from this container. Fires the beforeremove event before removing, then fires the remove event
431: * after the component has been removed.
432: *
433: * @param autoDestroy true to automatically invoke the component's destroy
434: */
435: public native void removeAll(boolean autoDestroy) /*-{
436: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
437: if(container.items){
438: var cs = container.items.items;
439: for(var i = 0, len = cs.length; i < len; i++) {
440: container.remove(cs[i], autoDestroy);
441: }
442: }
443: }-*/;
444:
445: /**
446: * Force this container's layout to be recalculated. A call to this function is required after adding a new component
447: * to an already rendered container. If you are not dynamically adding and removing components after render, this function will generally not need to be called.
448: */
449: public native void doLayout() /*-{
450: var container = this.@com.gwtext.client.widgets.Component::getOrCreateJsObj()();
451: container.doLayout();
452: }-*/;
453:
454: /**
455: * Add a Container listener.
456: *
457: * @param listener the listener
458: */
459: public native void addListener(ContainerListener listener) /*-{
460: this.@com.gwtext.client.widgets.BoxComponent::addListener(Lcom/gwtext/client/widgets/event/BoxComponentListener;)(listener);
461: var containerJ = this;
462:
463: this.@com.gwtext.client.widgets.Component::addListener(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)('add',
464: function(source, component, index) {
465: var componentJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(component);
466: listener.@com.gwtext.client.widgets.event.ContainerListener::onAdd(Lcom/gwtext/client/widgets/Container;Lcom/gwtext/client/widgets/Component;I)(containerJ, componentJ, index);
467: }
468: );
469:
470: this.@com.gwtext.client.widgets.Component::addListener(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)('beforeadd',
471: function(source, component, index) {
472: var componentJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(component);
473: return listener.@com.gwtext.client.widgets.event.ContainerListener::doBeforeAdd(Lcom/gwtext/client/widgets/Container;Lcom/gwtext/client/widgets/Component;I)(containerJ, componentJ, index);
474: }
475: );
476:
477: this.@com.gwtext.client.widgets.Component::addListener(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)('afterlayout',
478: function(source, layout) {
479: listener.@com.gwtext.client.widgets.event.ContainerListener::onAfterLayout(Lcom/gwtext/client/widgets/Container;)(containerJ);
480: }
481: );
482:
483: this.@com.gwtext.client.widgets.Component::addListener(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)('remove',
484: function(source, component) {
485: var componentJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(component);
486: listener.@com.gwtext.client.widgets.event.ContainerListener::onRemove(Lcom/gwtext/client/widgets/Container;Lcom/gwtext/client/widgets/Component;)(containerJ, componentJ);
487: }
488: );
489:
490: this.@com.gwtext.client.widgets.Component::addListener(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)('beforeremove',
491: function(source, component) {
492: var componentJ = @com.gwtext.client.widgets.ComponentFactory::getComponent(Lcom/google/gwt/core/client/JavaScriptObject;)(component);
493: return listener.@com.gwtext.client.widgets.event.ContainerListener::doBeforeRemove(Lcom/gwtext/client/widgets/Container;Lcom/gwtext/client/widgets/Component;)(containerJ, componentJ);
494: }
495: );
496: }-*/;
497:
498: // --- config properties ---
499:
500: /**
501: * A string component id or the numeric index of the component that should be initially activated within the
502: * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first item in
503: * the container's collection). activeItem only applies to layout styles that can display items one at a time
504: * (like {@link com.gwtext.client.widgets.layout.AccordionLayout} , {@link com.gwtext.client.widgets.layout.CardLayout}
505: * and {@link com.gwtext.client.widgets.layout.FitLayout} ).
506: *
507: * @param activeItem the active Item ID
508: */
509: public void setActiveItemID(String activeItem) {
510: //need local variable as bean introspectors will error out when underlting activeItem attribute can
511: //either be string or int
512: activeItemID = activeItem;
513: if (isRendered() && layout instanceof CardLayout) {
514: ((CardLayout) layout).setActiveItem(activeItem);
515: } else {
516: setAttribute("activeItem", activeItem, true);
517: }
518: }
519:
520: /**
521: * @return the active Item ID
522: */
523: public String getActiveItemID() {
524: return activeItemID;
525: }
526:
527: /**
528: * A string component id or the numeric index of the component that should be initially activated within the
529: * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first item in
530: * the container's collection). activeItem only applies to layout styles that can display items one at a time
531: * (like {@link com.gwtext.client.widgets.layout.AccordionLayout} , {@link com.gwtext.client.widgets.layout.CardLayout}
532: * and {@link com.gwtext.client.widgets.layout.FitLayout} ).
533: *
534: *
535: * @param activeItem the active Item ID
536: */
537: public void setActiveItem(int activeItem) {
538: if (isRendered() && layout instanceof CardLayout) {
539: ((CardLayout) layout).setActiveItem(activeItem);
540: } else {
541: setAttribute("activeItem", activeItem, true);
542: }
543: }
544:
545: /**
546: * Return the Active Item index.
547: *
548: * @return the active item index
549: */
550: public int getActiveItem() {
551: return JavaScriptObjectHelper.getAttributeAsInt(config,
552: "activeItem");
553: }
554:
555: /**
556: * If true the container will automatically destroy any contained component that is removed from it, else destruction
557: * must be handled manually (defaults to true).
558: *
559: * @param autoDestroy true to autodestroy
560: */
561: public void setAutoDestroy(boolean autoDestroy) {
562: setAttribute("autoDestroy", autoDestroy, true);
563: }
564:
565: /**
566: * @return true if Component is configured to auto destroy contained components on destruction
567: */
568: public boolean getAutoDestroy() {
569: return getAttributeAsBoolean("autoDestroy");
570: }
571:
572: /**
573: * When set to true (100 milliseconds), the layout assigned for this container will buffer
574: * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
575: * with a large amount of sub components that frequent calls to layout are expensive.
576: *
577: * @param bufferResize true to buffer resize
578: */
579: public void setBufferResize(boolean bufferResize) {
580: setAttribute("bufferResize", bufferResize, true);
581: }
582:
583: /**
584: * @return true if buffer resize is enabled
585: */
586: public boolean getBufferResize() {
587: return getAttributeAsBoolean("bufferResize");
588: }
589:
590: /**
591: * True to hide the borders of each contained component, false to defer to the component's existing border settings (defaults to false).
592: *
593: * @param hideBorders true to hide borders
594: */
595: public void setHideBorders(boolean hideBorders) {
596: setAttribute("hideBorders", hideBorders, true);
597: }
598:
599: /**
600: * True to hide the borders of each contained component, false to defer to the component's existing border settings (defaults to false).
601: *
602: * @return true if hide borders
603: */
604: public boolean getHideBorders() {
605: return getAttributeAsBoolean("hideBorders");
606: }
607:
608: /**
609: * The layout type to be used in this container. If not specified, a default ContainerLayout will be created and used.
610: *
611: * @param layout the layout to use
612: * @throws IllegalStateException this property cannot be changed after the Component has been rendered
613: */
614: public void setLayout(ContainerLayout layout)
615: throws IllegalStateException {
616: this .layout = layout;
617: if (layout.getSpacing() != null && this instanceof Panel) {
618: ((Panel) this ).setBorder(false);
619: }
620: setAttribute("layout", layout.getJsObj(), true);
621: if (layout.getContainerAttributes() != null) {
622: JavaScriptObjectHelper.apply(layout
623: .getContainerAttributes(), isCreated() ? config
624: : getJsObj());
625: }
626: }
627:
628: public ContainerLayout getLayout() {
629: return layout;
630: }
631:
632: /**
633: * True to automatically monitor window resize events to handle anything that is sensitive to the current size of the
634: * viewport. This value is typically managed by the chosen layout and should not need to be set manually.
635: *
636: * @param monitorResize true to monitor resize
637: * @throws IllegalStateException this property cannot be changed after the Component has been rendered
638: */
639: public void setMonitorResize(boolean monitorResize)
640: throws IllegalStateException {
641: setAttribute("monitorResize", monitorResize, true);
642: }
643:
644: /**
645: * @return true if monitor resize
646: */
647: public boolean getMonitorResize() {
648: return JavaScriptObjectHelper.getAttributeAsBoolean(config,
649: "monitorResize");
650: }
651: }
|