001: /* Box.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Mon Jun 20 21:51:32 2005, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2005 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zul;
020:
021: import java.util.Iterator;
022: import java.io.IOException;
023:
024: import org.zkoss.lang.JVMs;
025: import org.zkoss.lang.Objects;
026: import org.zkoss.xml.HTMLs;
027:
028: import org.zkoss.zk.ui.Component;
029: import org.zkoss.zk.ui.WrongValueException;
030: import org.zkoss.zk.ui.sys.ComponentCtrl;
031: import org.zkoss.zk.ui.ext.render.Floating;
032:
033: import org.zkoss.zul.impl.XulElement;
034: import org.zkoss.zul.impl.Utils;
035:
036: /**
037: * A box.
038: *
039: * @author tomyeh
040: */
041: public class Box extends XulElement {
042: private String _spacing;
043: private String _align = "start", _pack = "start";
044: /** Array of width/height for each cell. */
045: private String[] _sizes;
046:
047: /** Default: vertical ({@link Vbox}).
048: */
049: public Box() {
050: this ("vertical");
051: }
052:
053: /**
054: * @param orient either "horizontal" or "vertical".
055: */
056: public Box(String orient) {
057: setOrient(orient);
058: }
059:
060: /** Constructor a box by assigning an array of children.
061: *
062: * @param children an array of children to be added
063: * @since 2.4.0
064: */
065: public Box(Component[] children) {
066: this ("vertical", children);
067: }
068:
069: /** Constructor a box by assigning an array of children.
070: *
071: * @param children an array of children to be added
072: * @since 2.4.0
073: */
074: public Box(String orient, Component[] children) {
075: this (orient);
076:
077: if (children != null)
078: for (int j = 0; j < children.length; ++j)
079: appendChild(children[j]);
080: }
081:
082: /** Returns whether it is a horizontal box.
083: * @since 3.0.0
084: */
085: public boolean isHorizontal() {
086: return "horizontal".equals(getOrient());
087: }
088:
089: /** Returns whether it is a vertical box.
090: * @since 3.0.0
091: */
092: public boolean isVertical() {
093: return "vertical".equals(getOrient());
094: }
095:
096: /** Returns the orient (the same as {@link #getMold}).
097: * <p>Default: "vertical".
098: */
099: public String getOrient() {
100: return getMold();
101: }
102:
103: /** Sets the orient.
104: * @param orient either "horizontal" or "vertical".
105: */
106: public void setOrient(String orient) throws WrongValueException {
107: if (!"horizontal".equals(orient) && !"vertical".equals(orient))
108: throw new WrongValueException("orient cannot be " + orient);
109:
110: setMold(orient);
111: }
112:
113: /** Returns the spacing between adjacent children, or null if the default
114: * spacing is used.
115: *
116: * <p>The default spacing depends on the definition of the style class
117: * called "xxx-sp", where xxx is
118: *
119: * <ol>
120: * <li>{@link #getSclass} if it is not null.</li>
121: * <li>hbox if {@link #getSclass} is null and it is a horizontal box.</li>
122: * <li>vbox if {@link #getSclass} is null and it is a vertical box.</li>
123: * </ol>
124: *
125: * <p>Default: null (means to use the default spacing).
126: */
127: public String getSpacing() {
128: return _spacing;
129: }
130:
131: /** Sets the spacing between adjacent children.
132: * @param spacing the spacing (such as "0", "5px", "3pt" or "1em"),
133: * or null to use the default spacing
134: * @see #getSpacing
135: */
136: public void setSpacing(String spacing) {
137: if (spacing != null && spacing.length() == 0)
138: spacing = null;
139: if (!Objects.equals(_spacing, spacing)) {
140: _spacing = spacing;
141: invalidate();
142: }
143: }
144:
145: /** Returns the vertical alignment of the adjacent cells of a box
146: * (top, middle or bottom).
147: * <p>Default: null (i.e., use the browser default, usually middle).
148: * @deprecated As of release 3.0.0, since it is not compliant to XUL.
149: * Use {@link #getAlign} and {@link #getPack} instead.
150: */
151: public String getValign() {
152: return toValign(isVertical() ? getPack() : getAlign());
153: }
154:
155: /** Sets the vertical alignment of the adjacent cells of a box.
156: *
157: * @param valign the vertical alignment: top, middle and bottom.
158: * @deprecated As of release 3.0.0, since it is not compliant to XUL.
159: * Use {@link #setAlign} and {@link #setPack} instead.
160: */
161: public void setValign(String valign) {
162: valign = valign == null ? null : "top".equals(valign) ? "start"
163: : "middle".equals(valign) ? "center" : "bottom"
164: .equals(valign) ? "end" : valign;
165:
166: if (isVertical())
167: setPack(valign);
168: else
169: setAlign(valign);
170: }
171:
172: private static String toValign(String v) {
173: return v == null ? null : "start".equals(v) ? "top" : "center"
174: .equals(v) ? "middle" : "end".equals(v) ? "bottom" : v;
175: }
176:
177: private static String toHalign(String v) {
178: return v == null ? null : "start".equals(v) ? "left" : "end"
179: .equals(v) ? "right" : v;
180: }
181:
182: /** Returns the alignment of cells of a box in the 'opposite' direction
183: * (<i>null</i>, start, center, end).
184: *
185: * <p>Default: start</p>
186: *
187: * <p>The align attribute specifies how child elements of the box are aligned,
188: * when the size of the box is larger than the total size of the children. For
189: * boxes that have horizontal orientation, it specifies how its children will
190: * be aligned vertically. For boxes that have vertical orientation, it is used
191: * to specify how its children are algined horizontally. The pack attribute
192: * ({@link #getPack}) is
193: * related to the alignment but is used to specify the position in the
194: * opposite direction.
195: *
196: * <dl>
197: * <dt>start</dt>
198: * <dd>Child elements are aligned starting from the left or top edge of
199: * the box. If the box is larger than the total size of the children, the
200: * extra space is placed on the right or bottom side.</dd>
201: * <dt>center</dt>
202: * <dd>Extra space is split equally along each side of the child
203: * elements, resulting in the children being placed in the center of the box.</dd>
204: * <dt>end</dt>
205: * <dd>Child elements are placed on the right or bottom edge of the box. If
206: * the box is larger than the total size of the children, the extra space is
207: * placed on the left or top side.</dd>
208: * </dl>
209: *
210: * @since 3.0.0
211: */
212: public String getAlign() {
213: return _align;
214: }
215:
216: /** Sets the alignment of cells of this box in the 'opposite' direction
217: * (<i>null</i>, start, center, end).
218: *
219: * @param align the alignment in the 'opposite' direction.
220: * Allowed values: start, center, end.
221: * If empty or null, the browser's default is used
222: * (IE center and FF left, if vertical).
223: * @since 3.0.0
224: */
225: public void setAlign(String align) {
226: if (!Objects.equals(_align, align)) {
227: _align = align;
228: if (isVertical())
229: invalidate();
230: else
231: smartUpdate("valign", toValign(align));
232: }
233: }
234:
235: /** Returns the alignment of cells of this box
236: * (<i>null</i>, start, center, end).
237: *
238: * <p>Default: null.
239: *
240: * <p>The pack attribute specifies where child elements of the box are placed
241: * when the box is larger that the size of the children. For boxes with
242: * horizontal orientation, it is used to indicate the position of children
243: * horizontally. For boxes with vertical orientation, it is used to indicate
244: * the position of children vertically. The align attribute
245: * ({@link #getAlign})is used to specify
246: * the position in the opposite direction.
247: *
248: * <dl>
249: * <dt>start</dt>
250: * <dd>Child elements are aligned starting from the left or top edge of
251: * the box. If the box is larger than the total size of the children, the
252: * extra space is placed on the right or bottom side.</dd>
253: * <dt>center</dt>
254: * <dd>Extra space is split equally along each side of the child
255: * elements, resulting in the children being placed in the center of the box.</dd>
256: * <dt>end</dt>
257: * <dd>Child elements are placed on the right or bottom edge of the box. If
258: * the box is larger than the total size of the children, the extra space is
259: * placed on the left or top side.</dd>
260: * </dl>
261: *
262: * @since 3.0.0
263: */
264: public String getPack() {
265: return _pack;
266: }
267:
268: /** Sets the alignment of cells of this box
269: * (<i>null</i>, start, center, end).
270: *
271: * @param pack the alignment. Allowed values: start, center, end.
272: * If empty or null, the browser's default is used.
273: * @since 3.0.0
274: */
275: public void setPack(String pack) {
276: if (!Objects.equals(_pack, pack)) {
277: _pack = pack;
278: invalidate(); //generated to all cells
279: }
280: }
281:
282: /** Returns the widths/heights, which is a list of numbers separated by comma
283: * to denote the width/height of each cell in a box.
284: * If {@link Hbox} (i.e., {@link #getOrient} is horizontal),
285: * it is a list of widths.
286: * If {@link Vbox} (i.e., {@link #getOrient} is vertical),
287: * it is a list of heights.
288: *
289: * <p>It is the same as {@link #getHeights}.
290: *
291: * <p>Default: empty.
292: */
293: public String getWidths() {
294: return Utils.arrayToString(_sizes);
295: }
296:
297: /** Returns the heights/widths, which is a list of numbers separated by comma
298: * to denote the height/width of each cell in a box.
299: * If {@link Hbox} (i.e., {@link #getOrient} is horizontal),
300: * it is a list of widths.
301: * If {@link Vbox} (i.e., {@link #getOrient} is vertical),
302: * it is a list of heights.
303: *
304: * <p>It is the same as {@link #getWidths}.
305: *
306: * <p>Default: empty.
307: */
308: public String getHeights() {
309: return getWidths();
310: }
311:
312: /** Sets the widths/heights, which is a list of numbers separated
313: * by comma to denote the width/height of each cell in a box.
314: *
315: * <p>It is the same as {@link #setHeights}.
316: *
317: * <p>For example, "10%,20%,30%" means the second cell shall
318: * occupy 10% width, the second cell 20%, the third cell 30%,
319: * and the following cells don't specify any width.
320: *
321: * <p>Note: the splitters are ignored, i.e., they are not cells.
322: *
323: * <p>Another example, ",,30%" means the third cell shall occupy
324: * 30% width, and the rest of cells don't specify any width.
325: * Of course, the real widths depend on the interpretation of
326: * the browser.
327: */
328: public void setWidths(String widths) throws WrongValueException {
329: final String[] sizes = Utils.stringToArray(widths, null);
330: if (!Objects.equals(sizes, _sizes)) {
331: _sizes = sizes;
332: invalidate();
333: }
334: }
335:
336: /** Sets the widths/heights, which is a list of numbers separated
337: * by comma to denote the width/height of each cell in a box.
338: *
339: * <p>It is the same as {@link #setWidths}.
340: */
341: public void setHeights(String heights) throws WrongValueException {
342: setWidths(heights);
343: }
344:
345: /** Returns the outer attributes used to wrap the children (never null).
346: * It is used only for the vertical layout.
347: */
348: public String getChildOuterAttrs(Component child) {
349: final boolean vert = isVertical();
350: if (child instanceof Splitter)
351: return (vert ? " height" : " width") + "=\"8px\"";
352:
353: final StringBuffer sb = new StringBuffer(64)
354: .append(" z.coexist=\"true\"");
355: //coexist: the visibility of exterior is the same as child.
356:
357: //Note: visible is handled in getChildInnerAttrs if horizontal layout
358: if (vert) {
359: HTMLs.appendAttribute(sb, "valign", toValign(_pack));
360: if (!child.isVisible()) {
361: final Object xc = ((ComponentCtrl) child)
362: .getExtraCtrl();
363: if (!(xc instanceof Floating)
364: || !((Floating) xc).isFloating())
365: sb.append(" style=\"display:none\"");
366: }
367: }
368: return sb.toString();
369: }
370:
371: /** Returns the inner attributes used to wrap the children (never null).
372: * Used only by component development to generate HTML tags.
373: */
374: public String getChildInnerAttrs(Component child) {
375: if (child instanceof Splitter)
376: return "";
377:
378: final boolean vert = isVertical();
379: final StringBuffer sb = new StringBuffer(64);
380:
381: final String align = toHalign(vert ? _align : _pack);
382: if (align != null && align.length() > 0) {
383: HTMLs.appendAttribute(sb, "align", align);
384: }
385:
386: String size = null;
387: if (_sizes != null) {
388: int j = 0;
389: for (Iterator it = getChildren().iterator(); it.hasNext();) {
390: final Object o = it.next();
391: if (child == o) {
392: size = _sizes[j];
393: break;
394: } else if (!(o instanceof Splitter)) {
395: if (++j >= _sizes.length)
396: break; //not found
397: }
398: }
399: }
400:
401: final Object xc = ((ComponentCtrl) child).getExtraCtrl();
402: final boolean floating = (xc instanceof Floating)
403: && ((Floating) xc).isFloating();
404: final boolean visible = vert || floating || child.isVisible();
405: //if vert, visible is handled by getChildOutAttrs
406:
407: if (size != null || floating || !visible) {
408: sb.append(" style=\"");
409: if (!visible)
410: sb.append("display:none;");
411:
412: if (floating || size != null)
413: sb.append(vert ? "height" : "width").append(':')
414: .append(floating ? "0" : size);
415:
416: sb.append('"');
417: }
418: return sb.toString();
419: }
420:
421: /** Returns the attributes used by the 'cave' element (never null).
422: * Used only by component development to generate HTML tags.
423: * @since 3.0.0
424: */
425: public String getCaveAttrs() {
426: if (isVertical())
427: return "";
428:
429: final String valign = toValign(_align);
430: return valign != null ? " valign=\"" + valign + '"' : null;
431: }
432:
433: //-- Component --//
434: public boolean insertBefore(Component newChild, Component refChild) {
435: //Bug 1828702: onChildAdded not called if only moved
436: if (super .insertBefore(newChild, refChild)) {
437: invalidate();
438: return true;
439: }
440: return false;
441: }
442:
443: public void onChildRemoved(Component child) {
444: super .onChildRemoved(child);
445: invalidate();
446: }
447:
448: public void onDrawNewChild(Component child, StringBuffer out)
449: throws IOException {
450: throw new InternalError(); //impossible since we always invalidate
451: }
452: }
|