001: /* Treeitem.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Wed Jul 6 18:56:15 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.List;
022: import java.util.Iterator;
023:
024: import org.zkoss.lang.Objects;
025: import org.zkoss.lang.Strings;
026:
027: import org.zkoss.zk.ui.Component;
028: import org.zkoss.zk.ui.UiException;
029: import org.zkoss.zk.ui.ext.client.Openable;
030:
031: import org.zkoss.zul.impl.XulElement;
032:
033: /**
034: * A treeitem.
035: *
036: * <p>Event:
037: * <ol>
038: * <li>onOpen is sent when a tree item is opened or closed by user.</li>
039: * <li>onDoubleClick is sent when user double-clicks the treeitem.</li>
040: * <li>onRightClick is sent when user right-clicks the treeitem.</li>
041: * </ol>
042: *
043: * @author tomyeh
044: */
045: public class Treeitem extends XulElement {
046: private transient Treerow _treerow;
047: private transient Treechildren _treechildren;
048: private Object _value;
049: private boolean _open = true;
050: private boolean _selected = false;
051: private boolean _disabled = false;
052:
053: /** whether the content of this item is loaded; used if
054: * the tree owning this item is using a tree model.
055: */
056: private boolean _loaded = false;
057:
058: public Treeitem() {
059: }
060:
061: public Treeitem(String label) {
062: setLabel(label);
063: }
064:
065: public Treeitem(String label, Object value) {
066: setLabel(label);
067: setValue(value);
068: }
069:
070: /**
071: * Sets whether it is disabled.
072: * @since 3.0.1
073: */
074: public void setDisabled(boolean disabled) {
075: if (_disabled != disabled) {
076: _disabled = disabled;
077: invalidate();
078: }
079: }
080:
081: /** Returns whether it is disabled.
082: * <p>Default: false.
083: * @since 3.0.1
084: */
085: public boolean isDisabled() {
086: return _disabled;
087: }
088:
089: /**
090: * Return true whether this tree item is loaded
091: * @return true whether this tree item is loaded
092: * @since 3.0.0
093: */
094: public boolean isLoaded() {
095: return _loaded;
096: }
097:
098: /**
099: * Sets whether this tree item is loaded.
100: * @since 3.0.0
101: */
102: public void setLoaded(boolean loaded) {
103: _loaded = loaded;
104: }
105:
106: /**
107: * return the index of this container
108: * @return the index of this container
109: * @since 3.0.0
110: */
111: public int indexOf() {
112: List list = this .getParent().getChildren();
113: return list.indexOf(this );
114: }
115:
116: /** Returns the treerow that this tree item owns (might null).
117: * Each tree items has exactly one tree row.
118: */
119: public Treerow getTreerow() {
120: return _treerow;
121: }
122:
123: /** Returns the treechildren that this tree item owns, or null if
124: * doesn't have any child.
125: */
126: public Treechildren getTreechildren() {
127: return _treechildren;
128: }
129:
130: /** Returns whether the element is to act as a container
131: * which can have child elements.
132: */
133: public boolean isContainer() {
134: return _treechildren != null;
135: }
136:
137: /** Returns whether this element contains no child elements.
138: */
139: public boolean isEmpty() {
140: return _treechildren == null
141: || _treechildren.getChildren().isEmpty();
142: }
143:
144: /** Returns the value. It could be anything you want.
145: * <p>Default: null.
146: * <p>Note: the value is not sent to the browser, so it is OK to be
147: * anything.
148: */
149: public Object getValue() {
150: return _value;
151: }
152:
153: /** Sets the value.
154: * @param value the value.
155: * Note: the value is not sent to the browser, so it is OK to be
156: * anything.
157: */
158: public void setValue(Object value) {
159: if (!Objects.equals(_value, value)) {
160: _value = value;
161:
162: final Tree tree = getTree();
163: if (tree != null && tree.getName() != null)
164: smartUpdate("z.value", Objects.toString(_value));
165: }
166: }
167:
168: /** Returns whether this container is open.
169: * <p>Default: true.
170: */
171: public boolean isOpen() {
172: return _open;
173: }
174:
175: /** Sets whether this container is open.
176: */
177: public void setOpen(boolean open) {
178: if (_open != open) {
179: _open = open;
180: //Note: _treerow might not be ready yet because it might be
181: //initialized before creating child components (for ZK pages)
182: if (_treerow != null)
183: _treerow.smartUpdate("open", _open);
184: }
185: }
186:
187: /** Returns whether this item is selected.
188: */
189: public boolean isSelected() {
190: return _selected;
191: }
192:
193: /** Returns whether this item is selected.
194: */
195: public void setSelected(boolean selected) {
196: if (_selected != selected) {
197: final Tree tree = getTree();
198: if (tree != null) {
199: //Note: we don't update it here but let its parent does the job
200: tree.toggleItemSelection(this );
201: } else {
202: _selected = selected;
203: }
204: }
205: }
206:
207: /** Updates _selected directly and invalidate getTreerow if necessary.
208: */
209: /*package*/void setSelectedDirectly(boolean selected) {
210: _selected = selected;
211: }
212:
213: /** Returns the level this cell is. The root is level 0.
214: */
215: public int getLevel() {
216: int level = 0;
217: for (Component item = this ;; ++level) {
218: final Component tch = item.getParent();
219: if (tch == null)
220: break;
221:
222: item = tch.getParent();
223: if (item == null || item instanceof Tree)
224: break;
225: }
226: return level;
227: }
228:
229: /** Returns the label of the {@link Treecell} it contains, or null
230: * if no such cell.
231: */
232: public String getLabel() {
233: final Treecell cell = getFirstCell();
234: return cell != null ? cell.getLabel() : null;
235: }
236:
237: /** Sets the label of the {@link Treecell} it contains.
238: *
239: * <p>If it is not created, we automatically create it.
240: */
241: public void setLabel(String label) {
242: autoFirstCell().setLabel(label);
243: }
244:
245: private Treecell getFirstCell() {
246: return _treerow != null ? (Treecell) _treerow.getFirstChild()
247: : null;
248: }
249:
250: private Treecell autoFirstCell() {
251: if (_treerow == null) {
252: final Treerow row = new Treerow();
253: row.applyProperties();
254: row.setParent(this );
255: }
256:
257: Treecell cell = (Treecell) _treerow.getFirstChild();
258: if (cell == null) {
259: cell = new Treecell();
260: cell.applyProperties();
261: cell.setParent(_treerow);
262: }
263: return cell;
264: }
265:
266: /** Returns the src of the {@link Treecell} it contains, or null
267: * if no such cell.
268: */
269: public String getSrc() {
270: final Treecell cell = getFirstCell();
271: return cell != null ? cell.getSrc() : null;
272: }
273:
274: /** Sets the src of the {@link Treecell} it contains.
275: *
276: * <p>If it is not created, we automatically create it.
277: *
278: * <p>The same as {@link #setImage}.
279: */
280: public void setSrc(String src) {
281: autoFirstCell().setSrc(src);
282: }
283:
284: /** Returns the image of the {@link Treecell} it contains.
285: *
286: * <p>The same as {@link #getImage}.
287: */
288: public String getImage() {
289: return getSrc();
290: }
291:
292: /** Sets the image of the {@link Treecell} it contains.
293: *
294: * <p>If it is not created, we automatically create it.
295: *
296: * <p>The same as {@link #setSrc}.
297: */
298: public void setImage(String image) {
299: setSrc(image);
300: }
301:
302: /** Returns the parent tree item,
303: * or null if this item is already the top level of the tree.
304: * The parent tree item is actually the grandparent if any.
305: *
306: * @since 3.0.0
307: */
308: public Treeitem getParentItem() {
309: final Component p = getParent();
310: final Component gp = p != null ? p.getParent() : null;
311: return gp instanceof Treeitem ? (Treeitem) gp : null;
312: }
313:
314: /** Returns the nestest ancestor {@link Treeitem}, if any, or null if it is
315: * the top-level {@link Treeitem}. By top-level we mean the first level of
316: * a {@link Tree}.
317: *
318: * @deprecated As of release 2.4.1, replaced with {@link #getParentItem}
319: * to avoid confusion
320: */
321: public Treeitem getTreeitem() {
322: return getParentItem();
323: }
324:
325: /** Returns the tree owning this item.
326: */
327: public Tree getTree() {
328: for (Component p = this ; (p = p.getParent()) != null;)
329: if (p instanceof Tree)
330: return (Tree) p;
331: return null;
332: }
333:
334: /** Returns whether this item shall have the default focus. */
335: /*package*/boolean isFocusRequired() {
336: final Tree tree = getTree();
337: if (tree == null)
338: return false;
339:
340: final Treeitem sel = tree.getSelectedItem();
341: if (sel != null) {
342: return sel == this ;
343: } else {
344: final Iterator it = tree.getItems().iterator();
345: return it.hasNext() && it.next() == this ;
346: }
347: }
348:
349: //-- super --//
350:
351: //this is declared to make it accessible to Treerow
352: protected boolean isAsapRequired(String evtnm) {
353: return super .isAsapRequired(evtnm);
354: }
355:
356: public void smartUpdate(String attr, String value) {
357: if (_treerow != null)
358: _treerow.smartUpdate(attr, value);
359: }
360:
361: /** No callable. Use {@link Treerow#setDraggable} isntead.
362: */
363: public void setDraggable(String draggable) {
364: if (draggable != null)
365: throw new UnsupportedOperationException(
366: "Use Treerow.setDraggable() instead");
367: }
368:
369: /** No callable. Use {@link Treerow#setDroppable} isntead.
370: */
371: public void setDroppable(String dropable) {
372: if (dropable != null)
373: throw new UnsupportedOperationException(
374: "Use Treerow.setDroppable() instead");
375: }
376:
377: /** Not callable. Use {@link Treerow#setAction} instead.
378: */
379: public void setAction(String action) {
380: if (action != null)
381: throw new UnsupportedOperationException(
382: "Use Treerow.setAction() instead");
383: }
384:
385: //impl note: no getOuterAttrs, ON_OPEN..., since treeitem is virtual
386: //and the related codes are in treerow
387:
388: //-- Component --//
389: public void setParent(Component parent) {
390: final Component oldp = getParent();
391: if (oldp == parent)
392: return; //nothing changed
393:
394: if (parent != null && !(parent instanceof Treechildren))
395: throw new UiException("Wrong parent: " + parent);
396:
397: Treeitem affected = null; //what to invalidate
398: if (oldp != null) {
399: final List sibs = oldp.getChildren();
400: final int sz = sibs.size();
401: if (sz > 1 && sibs.get(sibs.size() - 1) == this )
402: affected = (Treeitem) sibs.get(sibs.size() - 2);
403: //we have to invalidate it because it will become last-child
404: }
405: final Tree oldtree = oldp != null ? getTree() : null;
406:
407: super .setParent(parent);
408:
409: if (affected != null && affected._treerow != null)
410: affected._treerow.invalidate(); //only the first row
411:
412: if (parent != null) {
413: final List sibs = parent.getChildren();
414: final int sz = sibs.size();
415: if (sz > 1 && sibs.get(sz - 1) == this ) {
416: affected = (Treeitem) sibs.get(sz - 2);
417: if (affected._treerow != null)
418: affected._treerow.invalidate(); //only the first row
419: }
420: }
421:
422: //maintain the selected status
423: if (oldtree != null)
424: oldtree.onTreeitemRemoved(this );
425: if (parent != null) {
426: final Tree tree = getTree();
427: if (tree != null)
428: tree.onTreeitemAdded(this );
429: }
430: }
431:
432: public boolean insertBefore(Component child, Component insertBefore) {
433: if (child instanceof Treerow) {
434: if (_treerow != null && _treerow != child)
435: throw new UiException("Only one treerow is allowed: "
436: + this );
437: _treerow = (Treerow) child;
438: } else if (child instanceof Treechildren) {
439: if (_treechildren != null && _treechildren != child)
440: throw new UiException(
441: "Only one treechildren is allowed: " + this );
442: _treechildren = (Treechildren) child;
443: if (_treerow != null)
444: _treerow.invalidate();
445: } else {
446: throw new UiException("Unsupported child for tree item: "
447: + child);
448: }
449: return super .insertBefore(child, insertBefore);
450: }
451:
452: public void onChildRemoved(Component child) {
453: if (child instanceof Treerow) {
454: _treerow = null;
455: } else if (child instanceof Treechildren) {
456: _treechildren = null;
457: }
458: super .onChildRemoved(child);
459: }
460:
461: public void invalidate() {
462: //There is no counter-part at client if no tree row
463: //We didn't set ATTR_NO_CHILD at insertBefore, since we cannot
464: //solve the issue that a treeitem without treerow:(
465: if (_treerow != null)
466: super .invalidate();
467: }
468:
469: //Cloneable//
470: public Object clone() {
471: final Treeitem clone = (Treeitem) super .clone();
472:
473: int cnt = 0;
474: if (clone._treerow != null)
475: ++cnt;
476: if (clone._treechildren != null)
477: ++cnt;
478: if (cnt > 0)
479: clone.afterUnmarshal(cnt);
480:
481: return clone;
482: }
483:
484: private void afterUnmarshal(int cnt) {
485: for (Iterator it = getChildren().iterator(); it.hasNext();) {
486: final Object child = it.next();
487: if (child instanceof Treerow) {
488: _treerow = (Treerow) child;
489: if (--cnt == 0)
490: break;
491: } else if (child instanceof Treechildren) {
492: _treechildren = (Treechildren) child;
493: if (--cnt == 0)
494: break;
495: }
496: }
497: }
498:
499: //-- Serializable --//
500: private synchronized void readObject(java.io.ObjectInputStream s)
501: throws java.io.IOException, ClassNotFoundException {
502: s.defaultReadObject();
503:
504: afterUnmarshal(-1);
505: }
506:
507: //-- ComponentCtrl --//
508: protected Object newExtraCtrl() {
509: return new ExtraCtrl();
510: }
511:
512: /** A utility class to implement {@link #getExtraCtrl}.
513: * It is used only by component developers.
514: */
515: protected class ExtraCtrl extends XulElement.ExtraCtrl implements
516: Openable {
517: //-- Openable --//
518: public void setOpenByClient(boolean open) {
519: _open = open;
520: /*
521: * Load Treeitem.
522: * SetOpen will trigger this function.
523: */
524: if (getTree() != null)
525: getTree().renderItem(Treeitem.this);
526: }
527: }
528: }
|