001: /*
002: * MyGWT Widget Library
003: * Copyright(c) 2007, MyGWT.
004: * licensing@mygwt.net
005: *
006: * http://mygwt.net/license
007: */
008: package net.mygwt.ui.client.widget;
009:
010: import java.util.HashMap;
011: import java.util.Map;
012:
013: import net.mygwt.ui.client.Events;
014: import net.mygwt.ui.client.MyDOM;
015: import net.mygwt.ui.client.util.Size;
016: import net.mygwt.ui.client.widget.layout.FlowLayout;
017:
018: import com.google.gwt.user.client.DOM;
019: import com.google.gwt.user.client.Element;
020: import com.google.gwt.user.client.ui.HTML;
021: import com.google.gwt.user.client.ui.Widget;
022:
023: /**
024: * A <code>Container</code> that lays out its children using a <code>Layout</code>.
025: *
026: * <dl>
027: * <dt><b>Events:</b></dt>
028: *
029: * <dd><b>BeforeAdd</b> : (widget, item, index)<br>
030: * <div>Fires before a item is added or inserted. Listeners can set the
031: * <code>doit</code> field to <code>false</code> to cancel the action.</div>
032: * <ul>
033: * <li>widget : this</li>
034: * <li>item : the item being added</li>
035: * <li>index : the index at which the item will be added</li>
036: * </ul>
037: * </dd>
038: *
039: * <dd><b>BeforeRemove</b> : (widget, item)<br>
040: * <div>Fires before a item is removed. Listeners can set the <code>doit</code>
041: * field to <code>false</code> to cancel the action.</div>
042: * <ul>
043: * <li>widget : this</li>
044: * <li>item : the item being removed</li>
045: * </ul>
046: * </dd>
047: *
048: * <dd><b>Add</b> : (widget, item, index)<br>
049: * <div>Fires after a item has been added or inserted.</div>
050: * <ul>
051: * <li>widget : this</li>
052: * <li>item : the item that was added</li>
053: * <li>index : the index at which the item will be added</li>
054: * </ul>
055: * </dd>
056: *
057: * <dd><b>Remove</b> : (widget, item)<br>
058: * <div>Fires after a item has been removed.</div>
059: * <ul>
060: * <li>widget : this</li>
061: * <li>item : the item being removed</li>
062: * </ul>
063: * </dd>
064: * </dl>
065: *
066: * @see Layout
067: */
068: public class WidgetContainer extends ScrollContainer {
069:
070: /**
071: * The container's layout.
072: */
073: protected Layout layout;
074:
075: /**
076: * The size at the last time the layout executed.
077: */
078: protected Size lastSize;
079:
080: /**
081: * disableLayout specifies if the container's layout is disabled. Default
082: * value is <code>false</code>.
083: */
084: protected boolean disableLayout = false;
085:
086: private boolean layoutOnChange = false;
087: private boolean monitorResize = true;
088: private Map layoutMap;
089:
090: /**
091: * Adds a widget to the container.
092: *
093: * @param widget the widget to add
094: */
095: public void add(Widget widget) {
096: insert(widget, getWidgetCount());
097: }
098:
099: /**
100: * Adds a widget the the container.
101: *
102: * @param widget the widget to add
103: * @param layoutData the layout data
104: */
105: public void add(Widget widget, Object layoutData) {
106: insert(widget, getWidgetCount(), layoutData);
107: }
108:
109: /**
110: * Sets the container's layout.
111: *
112: * @param layout the new layout
113: */
114: public void setLayout(Layout layout) {
115: this .layout = layout;
116: }
117:
118: /**
119: * Creates a new HTML instance and adds it to the container.
120: *
121: * @param text the html text
122: */
123: public void addText(String text) {
124: HTML html = new HTML(text);
125: add(html);
126: }
127:
128: /**
129: * Returns the layout which is associated with the container, or
130: * <code>null</code> if one has not been set.
131: *
132: * @return the container's layout or <code>null</code>
133: */
134: public Layout getLayout() {
135: return layout;
136: }
137:
138: /**
139: * Returns the widget's layout data.
140: *
141: * @param widget the widget
142: * @return the layout data
143: */
144: public Object getLayoutData(Widget widget) {
145: if (layoutMap == null) {
146: return null;
147: }
148: return layoutMap.get(widget);
149: }
150:
151: /**
152: * Returns <code>true</code> if the layout will be executed when widgets are
153: * added or removed.
154: *
155: * @return the layout on change state
156: */
157: public boolean getLayoutOnChange() {
158: return layoutOnChange;
159: }
160:
161: /**
162: * Override this method to specify the element the container's children will
163: * be inserted.
164: *
165: * @return the element to be used as the panel's container
166: */
167: public Element getLayoutTarget() {
168: return getElement();
169: }
170:
171: /**
172: * Returns the monitor resize state.
173: *
174: * @return <code>true</code> if resizing is being monitored
175: */
176: public boolean getMonitorResize() {
177: return monitorResize;
178: }
179:
180: /**
181: * Returns the index of the widget.
182: *
183: * @param widget the widget
184: * @return the index
185: */
186: public int indexOf(Widget widget) {
187: return items.indexOf(widget);
188: }
189:
190: /**
191: * Inserts a widget into the container.
192: *
193: * @param widget the widget to add
194: * @param index the insert location
195: */
196: public void insert(Widget widget, int index) {
197: insert(widget, index, null);
198: }
199:
200: /**
201: * Inserts a widget into the container.
202: *
203: * @param widget the widget to insert
204: * @param index the insert location
205: * @param layoutData the layout data
206: */
207: public void insert(Widget widget, int index, Object layoutData) {
208: if (fireEvent(Events.BeforeAdd, this , widget, index)) {
209: setLayoutData(widget, layoutData);
210: items.add(index, widget);
211:
212: if (isRendered() && layoutOnChange) {
213: layout(true);
214: }
215:
216: fireEvent(Events.Add, this , widget, index);
217: }
218: }
219:
220: /**
221: * Asks the layout to lay out the container's children. If a layout has not
222: * been set a <code>FlowLayout</code> will be used. If the size of the
223: * container has not changed since the last time layout was called it will not
224: * be execute. See {@link #layout(boolean)} to force the layout to execute.
225: */
226: public void layout() {
227: if (disableLayout) {
228: onResize(getOffsetWidth(), getOffsetHeight());
229: return;
230: }
231: if (layout == null) {
232: layout = new FlowLayout();
233: }
234: onLayout();
235: }
236:
237: /**
238: * Asks the layout to lay out the container's children. If a layout has not
239: * been set a <code>FlowLayout</code> will be used.
240: *
241: * @param force <code>true</code> to force the layout to execute
242: */
243: public void layout(boolean force) {
244: if (force) {
245: lastSize = null;
246: }
247: if (!rendered) {
248: render();
249: }
250: layout();
251: }
252:
253: public boolean remove(Widget widget) {
254: if (fireEvent(Events.BeforeRemove, this , widget)) {
255: boolean result = super .remove(widget);
256:
257: if (isRendered() && layoutOnChange) {
258: layout(true);
259: }
260:
261: fireEvent(Events.Remove, this , widget);
262: return result;
263: }
264: return false;
265: }
266:
267: /**
268: * Removes all widgets.
269: */
270: public void removeAll() {
271: int count = getWidgetCount();
272: for (int i = 0; i < count; i++) {
273: remove(getWidget(0));
274: }
275: }
276:
277: /**
278: * Removes the layout data for the specified widget.
279: *
280: * @param widget the widget
281: */
282: public void removeLayoutData(Widget widget) {
283: if (layoutMap != null) {
284: layoutMap.remove(widget);
285: }
286: }
287:
288: /**
289: * Sets the layout data for the widget.
290: *
291: * @param widget the widget
292: * @param layoutData the widget's layout data
293: */
294: public void setLayoutData(Widget widget, Object layoutData) {
295: if (layoutMap == null) {
296: layoutMap = new HashMap();
297: }
298: layoutMap.put(widget, layoutData);
299: }
300:
301: /**
302: * Specifies if the container's layout should be called when widgets are added
303: * or removed. Default value is <code>false</code>.
304: *
305: * @param layoutOnChange <code>true</code> to enable
306: */
307: public void setLayoutOnChange(boolean layoutOnChange) {
308: this .layoutOnChange = layoutOnChange;
309: }
310:
311: /**
312: * Sets the monitor resize state. When <code>true</code> the container's
313: * layout will be executed when the container is resized. Default value is
314: * <code>true</code>.
315: *
316: * @param monitorResize <code>true</code> to monitor resizing
317: */
318: public void setMonitorResize(boolean monitorResize) {
319: this .monitorResize = monitorResize;
320: }
321:
322: protected void onAttach() {
323: layout(true);
324: lastSize = null;
325: super .onAttach();
326: }
327:
328: protected void onLayout() {
329: if (getWidgetCount() > 0) {
330: Size size = MyDOM.getSize(getLayoutTarget());
331: int width = size.width;
332: int height = size.height;
333: if (lastSize != null) {
334: if (lastSize.width == width
335: && lastSize.height == height) {
336: return;
337: }
338: }
339: lastSize = new Size(width, height);
340: }
341: layout.layout(this );
342: }
343:
344: protected void onRender() {
345: setElement(DOM.createDiv());
346: setStyleAttribute("overflow", "hidden");
347: setStyleAttribute("position", "relative");
348: }
349:
350: protected void onResize(int width, int height) {
351: if (monitorResize && !disableLayout) {
352: layout();
353: }
354: }
355:
356: }
|