001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.ui;
021 
022 import js.ArrayLike;
023 import js.Id;
024 import js.Initializer;
025 import js.Js;
026 import js.ObjectLike;
027 import jsx.Configurable;
028 import jsx.core.ArrayLikes;
029 import jsx.core.Variables;
030 import jsx.ui.box.Frame;
031 import jsx.ui.event.Render;
032 import jsx.ui.event.Style;
033 
034 /**
035  * <p>A base class for container widgets.</p>
036  * <p>A container widget is a {@link Widget} that not only wraps a component that wraps 
037  * an HTML containing element, but also contains other widgets whose underlying HTML 
038  * elements are graphically contained by the HTML containing element wrapped by the 
039  * component of the container widget.</p>
040  * <p>A {@link Container} widget is {@link Configurable} and is also an event source which 
041  * fires {@link Widget.Event} events. It is meanwhile an event listener that handles 
042  * events fired from its underlying component.</p>
043  * 
044  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
045  */
046 public class Container extends Box<Widget>
047 {
048     private final static Id<Container> CONTAINER = new Id<Container>();
049 
050     /**
051      * <p>Returns the container source object if this one is graphically contained by 
052      * another widget.</p>
053      * <p>The wrapped object of a widget must also be a widget. This method simply returns 
054      * the configurable property {@link #CONTAINER} of this widget.</p>
055      * <p>The event queuing and dispatching mechanism of the superclass tries to dispatch 
056      * an event fired from a widget also to its container by calling this method, if this 
057      * source is graphically contained by another valid source and the event wants to 
058      * bubble, that is, its {@link jsx.Source.Event#BUBBLE} configurable property is <tt>true</tt>.</p>
059      * @return The value of the configurable property {@link #CONTAINER} of this widget.
060      * @since 1.0
061      */
062     public static final Container getContainer(Widget w) {
063         return ini(w).var(CONTAINER);
064     }
065 
066     /**
067      * <p>Sets the configurable property {@link #CONTAINER} of a specified widget.</p>
068      * @param w The widget to set configurable property {@link #CONTAINER} of.
069      * @param c The container to be set to the configurable property {@link #CONTAINER} of 
070      * the specified widget.
071      * @return The new value of the configurable property {@link #CONTAINER} of the 
072      * specified widget.
073      * @since 1.0
074      */
075     protected final static Container setContainer(Widget w, Container c) {
076         return ini(w).var(CONTAINER, c);
077     }
078     /**
079      * <p>A global identifier for a configurable property of a {@link Container} widget.</p>
080      * <p>The identified configurable property of a {@link Container} widget 
081      * is a reference to an array of widgets that are contained by or children of the 
082      * container.</p>
083      * @since 1.0
084      */
085     /**
086      * <p>A global identifier for a configurable property of a {@link Frame} widget.</p>
087      * <p>The identified configurable property of a {@link Frame} widget 
088      * is a reference to a {@link Layout} that graphically layouts the children of the 
089      * container.</p>
090      * @since 1.0
091      */
092     public final static Id<Layout> LAYOUT = new Id<Layout>();
093 
094     /**
095      * <p>Gets the layout manager that graphically layouts the children of the specified 
096      * container.</p>
097      * <p>This method simply returns the {@link #LAYOUT} property of the current container.</p>
098      * @param c The current container.
099      * @return The layout manager of the container.
100      * @since 1.0
101      */
102     public static final Layout getLayout(Container c) {
103         return ini(c).var(LAYOUT);
104     }
105 
106     private static final boolean needsLayout(Container c) {
107         ArrayLike<Widget> children = ini(c).var(CHILDREN);
108         return c.isRendered() && Js.be(getLayout(c)) &&  Js.be(children) && 
109             ArrayLikes.length(children) > 0;
110     }
111 
112     /**
113      * <p>Lays out the children of the current container widget.</p>
114      * <p>This method simply displays all the children of the specified container 
115      * by appending them to the container widget in the same sequence as in the children 
116      * list. It does nothing if the current container is not rendered or does not have a 
117      * child. A subclass should override this method to provide a particular layout.</p>
118      * <p>This method does nothing if the current container is not rendered or does not 
119      * have a layout manager and children.</p>
120      * @since 1.0
121      */
122     @Override
123     public final void layout() {
124         Layout u = getLayout(this);
125         if (Js.not(u)) {
126             super.layout();
127         } else if (needsLayout(this)) {
128             u.doLayout(this);
129         }
130     }
131     /**
132      * <p>Sets a layout manager for the current container.</p>
133      * @param ut The new layout manager.
134      * @since 1.0
135      */
136     public final void setLayout(Layout ut) {
137         ini(this).var(LAYOUT, ut);
138         Component e = unwrap();
139         ArrayLike<?> listeners = getListeners(e, Style.class);
140         for (int i = 0, len = ArrayLikes.length(listeners); i < len; i++) {
141             Object on = listeners.get(i);
142             if (on instanceof Container) {
143                 e.removeListener(Style.class, (Container)on);
144             }
145         }
146         e.addListener(Style.class, this);
147         layout();
148     }
149 
150     /**
151      * <p>Constructs a new container that wraps the specified component.</p>
152      * <p>This constructor invokes the typical constructor of this class passing 
153      * a newly created initializing object as the argument and specifying the argument 
154      * component as the wrapped one.</p>
155      * @param e The component to be wrapped by the container being created.
156      * @since 1.0
157      */
158     public Container(Component e) {
159         this(new Initializer().set(COMPONENT, e).var());
160     }
161 
162     /**
163      * <p>Constructs a new container with a specified layout manager.</p>
164      * <p>This constructor invokes the typical constructor of this class passing a newly 
165      * created initializing object as argument and sets the specified layout manger.</p>
166      * @param ut A layout manager.
167      * @since 1.0
168      */
169     public Container(Layout ut) {
170         this(new Initializer().set(
171                 LAYOUT, ut
172         ).var());
173     }
174 
175     /**
176      * <p>The default constructor.</p>
177      * <p>This constructor invokes the typical constructor {@link #Container(ObjectLike)} 
178      * passing a newly created initializing object as the argument.</p>
179      * @since 1.0
180      */
181     public Container() {
182         this(new Initializer().var());
183     }
184 
185     /**
186      * <p>A typical constructor forcing constructors of subclasses to pass 
187      * initializing data.</p>
188      * <p>This constructor invokes the typical constructor of the superclass passing 
189      * the specified initializing object as the argument, registers itself with the 
190      * underlying component to listener a {@link Render} event if it has not been 
191      * rendered. If the specified component is rendered, this constructor will force to 
192      * layout in the end.</p>
193      * @param ini The initializing object.
194      * @since 1.0
195      */
196     protected Container(ObjectLike ini) {
197         super(ini);
198     }
199 
200     @Override
201     protected final boolean append(Widget w) {
202         if (Js.not(w.getParent()) && Js.neq(w.unwrap(), Component.body())){
203             setContainer(w, this);
204             return super.append(w);
205         }
206         return false;
207     }
208 
209     /**
210      * <p>Removes a widget from the children list of the current container.</p>
211      * <p>This method will force a layout for the current box if the children list has been changed.</p>
212      * @param wt A widget to be removed from the children list of this container.
213      * @return <tt>true</tt> if the specified widget is successfully removed; <tt>false</tt>, 
214      * otherwise.
215      * @since 1.0
216      */
217     @Override
218     public final boolean remove(Widget wt) {
219         if (Js.eq(wt.getParent(), this)) {
220             detach(wt);
221             return super.remove(wt);
222         }
223         return false;
224     }
225 
226     private static final void detach(Widget wt) {
227         Component we = wt.unwrap();
228         Component.detach(we);
229         setContainer(wt, null);
230     }
231 
232     /**
233      * <p>Removes all children from the current container.</p>
234      * @since 1.0
235      */
236     public static final void removeAll(Container c) {
237         ArrayLike<Widget> children = ini(c).var(CHILDREN);
238         for (int i = 0, len = ArrayLikes.length(children); i < len; i++) {
239             detach(children.get(i));
240         }
241         ArrayLikes.removeAll(children);
242     }
243 
244     /**
245      * <p>Performs an action on the dispatched event.</p>
246      * <p>This method asks the layout manager to layout this container for resizing if 
247      * it has possibly been resized and has a layout manager.</p>
248      * @param evt The event dispatched to this listener.
249      * @since 1.0
250      */
251     @Override
252     public void onEvent(Style evt) {
253         if (Js.eq(unwrap(), Style.source(evt))) {
254             if (needsLayout(this)) {
255                 getLayout(this).onResize(this);
256             }
257         }
258     }
259 
260     /**
261      * <p>A base class that constructs default layout managers.</p>
262      * <p>A layout manager graphically layouts children for a container.</p>
263      * <p>Note, A layout manger is {@link Configurable}.</p>
264      * 
265      * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
266      */
267     public static abstract class Layout extends Configurable
268     {
269         /**
270          * <p>A global identifier for a configurable property of a {@link Layout} object.</p>
271          * <p>The identified configurable property of a {@link Layout} object refers to 
272          * the cached content width of the container the layout is in charge of.</p>
273          * @since 1.0
274          */
275         protected final static Id<Double> CONTENT_WIDTH  = new Id<Double>();
276         /**
277          * <p>A global identifier for a configurable property of a {@link Layout} object.</p>
278          * <p>The identified configurable property of a {@link Layout} object refers to 
279          * the cached content height of the container the layout is in charge of.</p>
280          * @since 1.0
281          */
282         protected final static Id<Double> CONTENT_HEIGHT = new Id<Double>();
283 
284         /**
285          * <p>Evaluates an integer number and returns 0 if the number is undefined.</p>
286          * @param i The integer number to evaluate.
287          * @return The value of the number or 0 if it is undefined.
288          * @since 1.0
289          */
290         protected static final int fix(Integer i) {
291             return Variables.undefined(i) ? 0 : i;
292         }
293 
294         /**
295          * <p>Updates the cached content width for a container.</p>
296          * @param c The container of which to update the cached content width.
297          * @return <tt>true</tt> if the cached width is updated; <tt>false</tt>, 
298          * otherwise.
299          * @since 1.0
300          */
301         protected static final boolean updateContentWidth(Container c) {
302             Double x1 = ini(c).var(CONTENT_WIDTH);
303             double x2 = Component.contentWidth(c.unwrap());
304             ini(c).var(CONTENT_WIDTH, x2);
305             return Js.neq(x1, x2);
306         }
307 
308         /**
309          * <p>Updates the cached content height for a container.</p>
310          * @param c The container of which to update the cached content height.
311          * @return <tt>true</tt> if the cached height is updated; <tt>false</tt>, 
312          * otherwise.
313          * @since 1.0
314          */
315         protected static final boolean updateContentHeight(Container c) {
316             Double x1 = ini(c).var(CONTENT_HEIGHT);
317             double x2 = Component.contentHeight(c.unwrap());
318             ini(c).var(CONTENT_HEIGHT, x2);
319             return Js.neq(x1, x2);
320         }
321 
322         /**
323          * <p>A typical constructor forcing constructors of subclasses to pass initializing data.
324          * <p>This constructor simply invokes the typical constructor of the superclass passing a 
325          * copy of the specified initializing object, or if the argument one is undefined, a newly 
326          * created {@link ObjectLike} object as the argument.</p>
327          * @param ini The initializing object that can also be created with an object literal. 
328          * @since 1.0
329          */
330         protected Layout(ObjectLike ini) {
331             super(Js.be(ini) ? Js.apply(
332                     new Initializer().var(),
333                     ini
334             ) : new Initializer().var());
335         }
336 
337         /**
338          * <p>The default constructor that creates a default layout manager.</p>
339          * <p>This constructor invokes the typical constructor of the superclass passing 
340          * the newly created initializing object as the argument without setting any 
341          * configurable properties.</p>
342          * @since 1.0
343          */
344         protected Layout() {
345             super(new Initializer().var());
346         }
347 
348         /**
349          * <p>Layouts a container.</p>
350          * <p>This method simply displays all the children of the specified container 
351          * by appending them to the container in the same sequence as in the children 
352          * list. A subclass should override this method to provide a particular layout.</p>
353          * @param c The container to layout.
354          * @since 1.0
355          */
356         protected abstract void doLayout(Container c);
357 
358         /**
359          * <p>Layouts a container when notified it has been resized.</p>
360          * <p>This method does nothing. A subclass should override this method to 
361          * perform an action if necessary.</p>
362          * @param c The container to layout on resizing.
363          * @since 1.0
364          */
365         protected abstract void onResize(Container c);
366     }
367 }