0001: /* Tree.java
0002:
0003: {{IS_NOTE
0004: Purpose:
0005:
0006: Description:
0007:
0008: History:
0009: Wed Jul 6 18:51:33 2005, Created by tomyeh
0010: }}IS_NOTE
0011:
0012: Copyright (C) 2005 Potix Corporation. All Rights Reserved.
0013:
0014: {{IS_RIGHT
0015: This program is distributed under GPL Version 2.0 in the hope that
0016: it will be useful, but WITHOUT ANY WARRANTY.
0017: }}IS_RIGHT
0018: */
0019: package org.zkoss.zul;
0020:
0021: import java.util.Arrays;
0022: import java.util.List;
0023: import java.util.Set;
0024: import java.util.LinkedHashSet;
0025: import java.util.Iterator;
0026: import java.util.ListIterator;
0027: import java.util.Collection;
0028: import java.util.Collections;
0029: import java.util.AbstractCollection;
0030: import java.util.ArrayList;
0031:
0032: import org.zkoss.lang.Exceptions;
0033: import org.zkoss.lang.Objects;
0034: import org.zkoss.util.logging.Log;
0035: import org.zkoss.xml.HTMLs;
0036:
0037: import org.zkoss.zk.ui.Component;
0038: import org.zkoss.zk.ui.UiException;
0039: import org.zkoss.zk.ui.WrongValueException;
0040: import org.zkoss.zk.ui.ext.client.Selectable;
0041: import org.zkoss.zk.ui.ext.client.InnerWidth;
0042: import org.zkoss.zk.ui.event.Event;
0043: import org.zkoss.zk.ui.event.EventListener;
0044: import org.zkoss.zk.ui.event.Events;
0045:
0046: //import org.zkoss.zul.Listbox.Renderer;
0047:
0048: import org.zkoss.zul.event.ListDataEvent;
0049: import org.zkoss.zul.event.TreeDataEvent;
0050: import org.zkoss.zul.event.TreeDataListener;
0051: import org.zkoss.zul.impl.XulElement;
0052:
0053: /**
0054: * A container which can be used to hold a tabular
0055: * or hierarchical set of rows of elements.
0056: *
0057: * <p>Event:
0058: * <ol>
0059: * <li>org.zkoss.zk.ui.event.SelectEvent is sent when user changes
0060: * the selection.</li>
0061: * </ol>
0062: *
0063: * <p>Default {@link #getSclass}: tree.
0064: *
0065: * @author tomyeh
0066: */
0067: public class Tree extends XulElement {
0068: private transient Treecols _treecols;
0069: private transient Treefoot _treefoot;
0070: private transient Treechildren _treechildren;
0071: /** A list of selected items. */
0072: private transient Set _selItems;
0073: /** The first selected item. */
0074: private transient Treeitem _sel;
0075: private transient Collection _heads;
0076: private int _rows = 0;
0077: /** The name. */
0078: private String _name;
0079: /** The style class prefix for generating icons. */
0080: private String _iconScls = "tree";
0081: /** # of items per page. */
0082: private int _pgsz = 10;
0083: private boolean _multiple, _checkmark;
0084: private boolean _vflex;
0085: /** disable smartUpdate; usually caused by the client. */
0086: private transient boolean _noSmartUpdate;
0087: private String _innerWidth = "100%";
0088:
0089: public Tree() {
0090: init();
0091: setSclass("tree");
0092: }
0093:
0094: private void init() {
0095: _selItems = new LinkedHashSet(5);
0096: _heads = new AbstractCollection() {
0097: public int size() {
0098: int sz = getChildren().size();
0099: if (_treechildren != null)
0100: --sz;
0101: if (_treefoot != null)
0102: --sz;
0103: return sz;
0104: }
0105:
0106: public Iterator iterator() {
0107: return new Iter();
0108: }
0109: };
0110: }
0111:
0112: /** Returns the treecols that this tree owns (might null).
0113: */
0114: public Treecols getTreecols() {
0115: return _treecols;
0116: }
0117:
0118: /** Returns the treefoot that this tree owns (might null).
0119: */
0120: public Treefoot getTreefoot() {
0121: return _treefoot;
0122: }
0123:
0124: /** Returns the treechildren that this tree owns (might null).
0125: */
0126: public Treechildren getTreechildren() {
0127: return _treechildren;
0128: }
0129:
0130: /** Returns a collection of heads, including {@link #getTreecols}
0131: * and auxiliary heads ({@link Auxhead}) (never null).
0132: *
0133: * @since 3.0.0
0134: */
0135: public Collection getHeads() {
0136: return _heads;
0137: }
0138:
0139: /** Returns the rows. Zero means no limitation.
0140: * <p>Default: 0.
0141: */
0142: public int getRows() {
0143: return _rows;
0144: }
0145:
0146: /** Sets the rows.
0147: * <p>Note: if both {@link #setHeight} is specified with non-empty,
0148: * {@link #setRows} is ignored
0149: */
0150: public void setRows(int rows) throws WrongValueException {
0151: if (rows < 0)
0152: throw new WrongValueException("Illegal rows: " + rows);
0153:
0154: if (_rows != rows) {
0155: _rows = rows;
0156: smartUpdate("z.size", Integer.toString(_rows));
0157: }
0158: }
0159:
0160: /** Returns the name of this component.
0161: * <p>Default: null.
0162: * <p>The name is used only to work with "legacy" Web application that
0163: * handles user's request by servlets.
0164: * It works only with HTTP/HTML-based browsers. It doesn't work
0165: * with other kind of clients.
0166: * <p>Don't use this method if your application is purely based
0167: * on ZK's event-driven model.
0168: */
0169: public String getName() {
0170: return _name;
0171: }
0172:
0173: /** Sets the name of this component.
0174: * <p>The name is used only to work with "legacy" Web application that
0175: * handles user's request by servlets.
0176: * It works only with HTTP/HTML-based browsers. It doesn't work
0177: * with other kind of clients.
0178: * <p>Don't use this method if your application is purely based
0179: * on ZK's event-driven model.
0180: *
0181: * @param name the name of this component.
0182: */
0183: public void setName(String name) {
0184: if (name != null && name.length() == 0)
0185: name = null;
0186: if (!Objects.equals(_name, name)) {
0187: _name = name;
0188: if (_name != null)
0189: smartUpdate("z.name", _name);
0190: else
0191: invalidate(); //1) generate _value; 2) add submit listener
0192: }
0193: }
0194:
0195: /** Returns whether the check mark shall be displayed in front
0196: * of each item.
0197: * <p>Default: false.
0198: */
0199: public final boolean isCheckmark() {
0200: return _checkmark;
0201: }
0202:
0203: /** Sets whether the check mark shall be displayed in front
0204: * of each item.
0205: * <p>The check mark is a checkbox if {@link #isMultiple} returns
0206: * true. It is a radio button if {@link #isMultiple} returns false.
0207: */
0208: public void setCheckmark(boolean checkmark) {
0209: if (_checkmark != checkmark) {
0210: _checkmark = checkmark;
0211: invalidate();
0212: }
0213: }
0214:
0215: /** Returns whether to grow and shrink vertical to fit their given space,
0216: * so called vertial flexibility.
0217: *
0218: * <p>Note: this attribute is ignored if {@link #setRows} is specified
0219: *
0220: * <p>Default: false.
0221: */
0222: public final boolean isVflex() {
0223: return _vflex;
0224: }
0225:
0226: /** Sets whether to grow and shrink vertical to fit their given space,
0227: * so called vertial flexibility.
0228: *
0229: * <p>Note: this attribute is ignored if {@link #setRows} is specified
0230: */
0231: public void setVflex(boolean vflex) {
0232: if (_vflex != vflex) {
0233: _vflex = vflex;
0234: smartUpdate("z.flex", _vflex);
0235: }
0236: }
0237:
0238: /**
0239: * Sets the inner width of this component.
0240: * The inner width is the width of the inner table.
0241: * By default, it is 100%. That is, it is the same as the width
0242: * of this component. However, it is changed when the user
0243: * is sizing the column's width.
0244: *
0245: * <p>Application developers rarely call this method, unless
0246: * they want to preserve the widths of sizable columns
0247: * changed by the user.
0248: * To preserve the widths, the developer have to store the widths of
0249: * all columns and the inner width ({@link #getInnerWidth}),
0250: * and then restore them when re-creating this component.
0251: *
0252: * @param innerWidth the inner width. If null, "100%" is assumed.
0253: * @since 3.0.0
0254: */
0255: public void setInnerWidth(String innerWidth) {
0256: if (innerWidth == null)
0257: innerWidth = "100%";
0258: if (!_innerWidth.equals(innerWidth)) {
0259: _innerWidth = innerWidth;
0260: smartUpdate("z.innerWidth", innerWidth);
0261: }
0262: }
0263:
0264: /**
0265: * Returns the inner width of this component.
0266: * The inner width is the width of the inner table.
0267: * <p>Default: "100%"
0268: * @see #setInnerWidth
0269: * @since 3.0.0
0270: */
0271: public String getInnerWidth() {
0272: return _innerWidth;
0273: }
0274:
0275: /** Returns the seltype.
0276: * <p>Default: "single".
0277: */
0278: public String getSeltype() {
0279: return _multiple ? "multiple" : "single";
0280: }
0281:
0282: /** Sets the seltype.
0283: * Currently, only "single" is supported.
0284: */
0285: public void setSeltype(String seltype) throws WrongValueException {
0286: if ("single".equals(seltype))
0287: setMultiple(false);
0288: else if ("multiple".equals(seltype))
0289: setMultiple(true);
0290: else
0291: throw new WrongValueException("Unknown seltype: " + seltype);
0292: }
0293:
0294: /** Returns whether multiple selections are allowed.
0295: * <p>Default: false.
0296: */
0297: public boolean isMultiple() {
0298: return _multiple;
0299: }
0300:
0301: /** Sets whether multiple selections are allowed.
0302: */
0303: public void setMultiple(boolean multiple) {
0304: if (_multiple != multiple) {
0305: _multiple = multiple;
0306: if (!_multiple && _selItems.size() > 1) {
0307: final Treeitem item = getSelectedItem();
0308: for (Iterator it = _selItems.iterator(); it.hasNext();) {
0309: final Treeitem ti = (Treeitem) it.next();
0310: if (ti != item) {
0311: ti.setSelectedDirectly(false);
0312: it.remove();
0313: }
0314: }
0315: //No need to update z.selId because z.multiple will do the job
0316: }
0317: if (isCheckmark())
0318: invalidate(); //change check mark
0319: else
0320: smartUpdate("z.multiple", _multiple);
0321: }
0322: }
0323:
0324: /** Returns the ID of the selected item (it is stored as the z.selId
0325: * attribute of the tree).
0326: */
0327: private String getSelectedId() {
0328: //NOTE: Treerow's uuid; not Treeitem's
0329: final Treerow tr = _sel != null ? _sel.getTreerow() : null;
0330: return tr != null ? tr.getUuid() : "zk_n_a";
0331: }
0332:
0333: /** Returns a readonly list of all descending {@link Treeitem}
0334: * (children's children and so on).
0335: *
0336: * <p>Note: the performance of the size method of returned collection
0337: * is no good.
0338: */
0339: public Collection getItems() {
0340: return _treechildren != null ? _treechildren.getItems()
0341: : Collections.EMPTY_LIST;
0342: }
0343:
0344: /** Returns the number of child {@link Treeitem}.
0345: * The same as {@link #getItems}.size().
0346: * <p>Note: the performance of this method is no good.
0347: */
0348: public int getItemCount() {
0349: return _treechildren != null ? _treechildren.getItemCount() : 0;
0350: }
0351:
0352: /** Deselects all of the currently selected items and selects
0353: * the given item.
0354: * <p>It is the same as {@link #setSelectedItem}.
0355: * @param item the item to select. If null, all items are deselected.
0356: */
0357: public void selectItem(Treeitem item) {
0358: if (item == null) {
0359: clearSelection();
0360: } else {
0361: if (item.getTree() != this )
0362: throw new UiException("Not a child: " + item);
0363:
0364: if (_sel != item || (_multiple && _selItems.size() > 1)) {
0365: for (Iterator it = _selItems.iterator(); it.hasNext();) {
0366: final Treeitem ti = (Treeitem) it.next();
0367: ti.setSelectedDirectly(false);
0368: }
0369: _selItems.clear();
0370:
0371: _sel = item;
0372: item.setSelectedDirectly(true);
0373: _selItems.add(item);
0374:
0375: final Treerow tr = item.getTreerow();
0376: if (tr != null)
0377: smartUpdate("select", tr.getUuid());
0378: }
0379: }
0380: }
0381:
0382: /** Selects the given item, without deselecting any other items
0383: * that are already selected..
0384: */
0385: public void addItemToSelection(Treeitem item) {
0386: if (item.getTree() != this )
0387: throw new UiException("Not a child: " + item);
0388:
0389: if (!item.isSelected()) {
0390: if (!_multiple) {
0391: selectItem(item);
0392: } else {
0393: item.setSelectedDirectly(true);
0394: _selItems.add(item);
0395: smartUpdateSelection();
0396: if (fixSelected())
0397: smartUpdate("z.selId", getSelectedId());
0398: }
0399: }
0400: }
0401:
0402: /** Deselects the given item without deselecting other items.
0403: */
0404: public void removeItemFromSelection(Treeitem item) {
0405: if (item.getTree() != this )
0406: throw new UiException("Not a child: " + item);
0407:
0408: if (item.isSelected()) {
0409: if (!_multiple) {
0410: clearSelection();
0411: } else {
0412: item.setSelectedDirectly(false);
0413: _selItems.remove(item);
0414: smartUpdateSelection();
0415: if (fixSelected())
0416: smartUpdate("z.selId", getSelectedId());
0417: //No need to use response because such info is carried on tags
0418: }
0419: }
0420: }
0421:
0422: /** Note: we have to update all selection at once, since addItemToSelection
0423: * and removeItemFromSelection might be called interchangeably.
0424: */
0425: private void smartUpdateSelection() {
0426: final StringBuffer sb = new StringBuffer(80);
0427: for (Iterator it = _selItems.iterator(); it.hasNext();) {
0428: final Treeitem item = (Treeitem) it.next();
0429: final Treerow tr = item.getTreerow();
0430: if (tr != null) {
0431: if (sb.length() > 0)
0432: sb.append(',');
0433: sb.append(tr.getUuid());
0434: }
0435: }
0436: smartUpdate("chgSel", sb.toString());
0437: }
0438:
0439: /** If the specified item is selected, it is deselected.
0440: * If it is not selected, it is selected. Other items in the tree
0441: * that are selected are not affected, and retain their selected state.
0442: */
0443: public void toggleItemSelection(Treeitem item) {
0444: if (item.isSelected())
0445: removeItemFromSelection(item);
0446: else
0447: addItemToSelection(item);
0448: }
0449:
0450: /** Clears the selection.
0451: */
0452: public void clearSelection() {
0453: if (!_selItems.isEmpty()) {
0454: for (Iterator it = _selItems.iterator(); it.hasNext();) {
0455: final Treeitem item = (Treeitem) it.next();
0456: item.setSelectedDirectly(false);
0457: }
0458: _selItems.clear();
0459: _sel = null;
0460: smartUpdate("select", "");
0461: }
0462: }
0463:
0464: /** Selects all items.
0465: */
0466: public void selectAll() {
0467: if (!_multiple)
0468: throw new UiException(
0469: "Appliable only to the multiple seltype: " + this );
0470:
0471: //we don't invoke getItemCount first because it is slow!
0472: boolean changed = false, first = true;
0473: for (Iterator it = getItems().iterator(); it.hasNext();) {
0474: final Treeitem item = (Treeitem) it.next();
0475: if (!item.isSelected()) {
0476: _selItems.add(item);
0477: item.setSelectedDirectly(true);
0478: changed = true;
0479: }
0480: if (first) {
0481: _sel = item;
0482: first = false;
0483: }
0484: }
0485: smartUpdate("selectAll", "true");
0486: }
0487:
0488: /** Returns the selected item.
0489: */
0490: public Treeitem getSelectedItem() {
0491: return _sel;
0492: }
0493:
0494: /** Deselects all of the currently selected items and selects
0495: * the given item.
0496: * <p>It is the same as {@link #selectItem}.
0497: */
0498: public void setSelectedItem(Treeitem item) {
0499: selectItem(item);
0500: }
0501:
0502: /** Returns all selected items.
0503: */
0504: public Set getSelectedItems() {
0505: return Collections.unmodifiableSet(_selItems);
0506: }
0507:
0508: /** Returns the number of items being selected.
0509: */
0510: public int getSelectedCount() {
0511: return _selItems.size();
0512: }
0513:
0514: /** Clears all child tree items ({@link Treeitem}.
0515: * <p>Note: after clear, {@link #getTreechildren} won't be null, but
0516: * it has no child
0517: */
0518: public void clear() {
0519: if (_treechildren == null)
0520: return;
0521:
0522: final List l = _treechildren.getChildren();
0523: if (l.isEmpty())
0524: return; //nothing to do
0525:
0526: for (Iterator it = new ArrayList(l).iterator(); it.hasNext();)
0527: ((Component) it.next()).detach();
0528: }
0529:
0530: /** Returns the page size that is used by all {@link Treechildren}
0531: * to display a portion of their child {@link Treeitem},
0532: * or -1 if no limitation.
0533: *
0534: * <p>Default: 10.
0535: *
0536: * @since 2.4.1
0537: */
0538: public int getPageSize() {
0539: return _pgsz;
0540: }
0541:
0542: /** Sets the page size that is used by all {@link Treechildren}
0543: * to display a portion of their child {@link Treeitem}.
0544: *
0545: * @param size the page size. If non-positive, there won't be
0546: * any limitation. In other wordss, all {@link Treeitem} are shown.
0547: * Notice: since the browser's JavaScript engine is slow to
0548: * handle huge trees, it is better not to set a non-positive size
0549: * if your tree is huge.
0550: * @since 2.4.1
0551: */
0552: public void setPageSize(int size) throws WrongValueException {
0553: if (size <= 0)
0554: size = -1; //no limitation
0555: if (_pgsz != size) {
0556: _pgsz = size;
0557: updateActivePageChildren(_treechildren);
0558: invalidate();
0559: //FUTURE: trade-off: search and update only
0560: //necessary Treechildren is faster or not
0561: }
0562: }
0563:
0564: /** Updates the active page for all tree children if necessary.
0565: */
0566: private static void updateActivePageChildren(Treechildren tc) {
0567: if (tc != null) {
0568: if (tc.getPageSizeDirectly() == 0) {
0569: final int pgcnt = tc.getPageCount();
0570: if (tc.getActivePage() >= pgcnt)
0571: tc.setActivePageDirectly(pgcnt - 1);
0572: //no need to invalidate since the whole tree is change
0573: }
0574:
0575: for (Iterator it = tc.getChildren().iterator(); it
0576: .hasNext();) {
0577: final Treeitem ti = (Treeitem) it.next();
0578: updateActivePageChildren(ti.getTreechildren()); //recursive
0579: }
0580: }
0581: }
0582:
0583: /** Returns the style class prefix used to generate the icons of this tree.
0584: *
0585: * <p>Default: tree.</br>
0586: * Another builtin style class: dottree (the style used prior 3.0).
0587: *
0588: * <p>Assume that the icon style class is <code>tree</code>, then
0589: * the following style classes are used for the icons of each tree item:
0590: * <dl>
0591: * <dt>tree-root-open</dt>
0592: * <dd>The icon used to represent the open state for tree items at the root level.</dd>
0593: * <dt>tree-root-close</dt>
0594: * <dd>The icon used to represent the close state for tree items at the root level.</dd>
0595: * <dt>tree-tee-open</dt>
0596: * <dd>The icon used to represent the open state for tree items that have next siblings.</dd>
0597: * <dt>tree-tee-close</dt>
0598: * <dd>The icon used to represent the close state for tree items at have next siblings.</dd>
0599: * <dt>tree-last-open</dt>
0600: * <dd>The icon used to represent the open state for tree items that don't have next siblings.</dd>
0601: * <dt>tree-last-close</dt>
0602: * <dd>The icon used to represent the close state for tree items at don't have next siblings.</dd>
0603: * <dt>tree-tee</dt>
0604: * <dd>The icon used to represent the T-shape icon.</dd>
0605: * <dt>tree-vbar</dt>
0606: * <dd>The icon used to represent the |-shape (vertical bar) icon.</dd>
0607: * <dt>tree-last</dt>
0608: * <dd>The icon used to represent the L-shape icon -- no next sibling.</dd>
0609: * <dt>tree-spacer</dt>
0610: * <dd>The icon used to represent the blank icon.</dd>
0611: * </dl>
0612: *
0613: * @since 3.0.0
0614: */
0615: public String getIconSclass() {
0616: return _iconScls;
0617: }
0618:
0619: /** Sets the style class prefix used to generate the icons of this tree.
0620: *
0621: * @since 3.0.0
0622: * @see #getIconSclass
0623: */
0624: public void setIconSclass(String scls) {
0625: if (!Objects.equals(_iconScls, scls)) {
0626: _iconScls = scls;
0627: invalidate();
0628: }
0629: }
0630:
0631: //-- Component --//
0632: public void smartUpdate(String attr, String value) {
0633: if (!_noSmartUpdate)
0634: super .smartUpdate(attr, value);
0635: }
0636:
0637: public boolean insertBefore(Component newChild, Component refChild) {
0638: if (newChild instanceof Treecols) {
0639: if (_treecols != null && _treecols != newChild)
0640: throw new UiException("Only one treecols is allowed: "
0641: + this );
0642: _treecols = (Treecols) newChild;
0643: } else if (newChild instanceof Treefoot) {
0644: if (_treefoot != null && _treefoot != newChild)
0645: throw new UiException("Only one treefoot is allowed: "
0646: + this );
0647: _treefoot = (Treefoot) newChild;
0648: } else if (newChild instanceof Treechildren) {
0649: if (_treechildren != null && _treechildren != newChild)
0650: throw new UiException(
0651: "Only one treechildren is allowed: " + this );
0652: _treechildren = (Treechildren) newChild;
0653: fixSelectedSet();
0654: } else if (!(newChild instanceof Auxhead)) {
0655: throw new UiException("Unsupported newChild: " + newChild);
0656: }
0657:
0658: if (super .insertBefore(newChild, refChild)) {
0659: //not need to invalidate since auxhead visible only with _treecols
0660: if (!(newChild instanceof Auxhead))
0661: invalidate();
0662: return true;
0663: }
0664: return false;
0665: }
0666:
0667: /** Called by {@link Treeitem} when is added to a tree. */
0668: /*package*/void onTreeitemAdded(Treeitem item) {
0669: fixNewChild(item);
0670: onTreechildrenAdded(item.getTreechildren());
0671: }
0672:
0673: /** Called by {@link Treeitem} when is removed from a tree. */
0674: /*package*/void onTreeitemRemoved(Treeitem item) {
0675: boolean fixSel = false;
0676: if (item.isSelected()) {
0677: _selItems.remove(item);
0678: fixSel = _sel == item;
0679: if (fixSel && !_multiple) {
0680: _sel = null;
0681: smartUpdate("z.selId", getSelectedId());
0682: assert _selItems.isEmpty();
0683: }
0684: }
0685: onTreechildrenRemoved(item.getTreechildren());
0686: if (fixSel)
0687: fixSelected();
0688: }
0689:
0690: /** Called by {@link Treechildren} when is added to a tree. */
0691: /*package*/void onTreechildrenAdded(Treechildren tchs) {
0692: if (tchs == null || tchs.getParent() == this )
0693: return; //already being processed by insertBefore
0694:
0695: //main the selected status
0696: for (Iterator it = tchs.getItems().iterator(); it.hasNext();)
0697: fixNewChild((Treeitem) it.next());
0698: }
0699:
0700: /** Fixes the status of new added child. */
0701: private void fixNewChild(Treeitem item) {
0702: if (item.isSelected()) {
0703: if (_sel != null && !_multiple) {
0704: item.setSelectedDirectly(false);
0705: item.invalidate();
0706: } else {
0707: if (_sel == null)
0708: _sel = item;
0709: _selItems.add(item);
0710: smartUpdate("z.selId", getSelectedId());
0711: }
0712: }
0713: }
0714:
0715: /** Called by {@link Treechildren} when is removed from a tree. */
0716: /*package*/void onTreechildrenRemoved(Treechildren tchs) {
0717: if (tchs == null || tchs.getParent() == this )
0718: return; //already being processed by onChildRemoved
0719:
0720: //main the selected status
0721: boolean fixSel = false;
0722: for (Iterator it = tchs.getItems().iterator(); it.hasNext();) {
0723: final Treeitem item = (Treeitem) it.next();
0724: if (item.isSelected()) {
0725: _selItems.remove(item);
0726: if (_sel == item) {
0727: if (!_multiple) {
0728: _sel = null;
0729: smartUpdate("z.selId", getSelectedId());
0730: assert _selItems.isEmpty();
0731: return; //done
0732: }
0733: fixSel = true;
0734: }
0735: }
0736: }
0737: if (fixSel)
0738: fixSelected();
0739: }
0740:
0741: public void onChildAdded(Component child) {
0742: super .onChildAdded(child);
0743: invalidate();
0744: }
0745:
0746: public void onChildRemoved(Component child) {
0747: if (child instanceof Treecols) {
0748: _treecols = null;
0749: } else if (child instanceof Treefoot) {
0750: _treefoot = null;
0751: } else if (child instanceof Treechildren) {
0752: _treechildren = null;
0753: _selItems.clear();
0754: _sel = null;
0755: }
0756: super .onChildRemoved(child);
0757: invalidate();
0758: }
0759:
0760: /** Fixes all info about the selected status. */
0761: private void fixSelectedSet() {
0762: _sel = null;
0763: _selItems.clear();
0764: for (Iterator it = getItems().iterator(); it.hasNext();) {
0765: final Treeitem item = (Treeitem) it.next();
0766: if (item.isSelected()) {
0767: if (_sel == null) {
0768: _sel = item;
0769: } else if (!_multiple) {
0770: item.setSelectedDirectly(false);
0771: continue;
0772: }
0773: _selItems.add(item);
0774: }
0775: }
0776: }
0777:
0778: /** Make _sel to be the first selected item. */
0779: private boolean fixSelected() {
0780: Treeitem sel = null;
0781: switch (_selItems.size()) {
0782: case 1:
0783: sel = (Treeitem) _selItems.iterator().next();
0784: case 0:
0785: break;
0786: default:
0787: for (Iterator it = getItems().iterator(); it.hasNext();) {
0788: final Treeitem item = (Treeitem) it.next();
0789: if (item.isSelected()) {
0790: sel = item;
0791: break;
0792: }
0793: }
0794: }
0795:
0796: if (sel != _sel) {
0797: _sel = sel;
0798: return true;
0799: }
0800: return false;
0801: }
0802:
0803: //-- super --//
0804: public String getOuterAttrs() {
0805: final StringBuffer sb = new StringBuffer(64).append(super
0806: .getOuterAttrs());
0807: HTMLs.appendAttribute(sb, "z.name", _name);
0808: HTMLs.appendAttribute(sb, "z.size", getRows());
0809: HTMLs.appendAttribute(sb, "z.selId", getSelectedId());
0810: if (_multiple)
0811: HTMLs.appendAttribute(sb, "z.multiple", true);
0812: //if (_checkmark)
0813: // HTMLs.appendAttribute(sb, "z.checkmark", _checkmark);
0814: if (_vflex)
0815: HTMLs.appendAttribute(sb, "z.vflex", true);
0816: appendAsapAttr(sb, Events.ON_SELECT);
0817:
0818: final Treechildren tc = getTreechildren();
0819: if (tc != null) {
0820: HTMLs.appendAttribute(sb, "z.tchsib", tc.getUuid());
0821: //we have to generate first, since # of page might grow later
0822:
0823: final int pgcnt = tc.getPageCount();
0824: if (pgcnt > 1) {
0825: HTMLs.appendAttribute(sb, "z.pgc", pgcnt);
0826: HTMLs.appendAttribute(sb, "z.pgi", tc.getActivePage());
0827: HTMLs.appendAttribute(sb, "z.pgsz", tc.getPageSize());
0828: }
0829: }
0830: return sb.toString();
0831: }
0832:
0833: //Cloneable//
0834: public Object clone() {
0835: int cntSel = _selItems.size();
0836:
0837: final Tree clone = (Tree) super .clone();
0838: clone.init();
0839:
0840: int cnt = 0;
0841: if (_treecols != null)
0842: ++cnt;
0843: if (_treefoot != null)
0844: ++cnt;
0845: if (_treechildren != null)
0846: ++cnt;
0847: if (cnt > 0 || cntSel > 0)
0848: clone.afterUnmarshal(cnt, cntSel);
0849:
0850: return clone;
0851: }
0852:
0853: /** @param cnt # of children that need special handling (used for optimization).
0854: * -1 means process all of them
0855: * @param cntSel # of selected items
0856: */
0857: private void afterUnmarshal(int cnt, int cntSel) {
0858: if (cnt != 0) {
0859: for (Iterator it = getChildren().iterator(); it.hasNext();) {
0860: final Object child = it.next();
0861: if (child instanceof Treecols) {
0862: _treecols = (Treecols) child;
0863: if (--cnt == 0)
0864: break;
0865: } else if (child instanceof Treefoot) {
0866: _treefoot = (Treefoot) child;
0867: if (--cnt == 0)
0868: break;
0869: } else if (child instanceof Treechildren) {
0870: _treechildren = (Treechildren) child;
0871: if (--cnt == 0)
0872: break;
0873: }
0874: }
0875: }
0876:
0877: _sel = null;
0878: _selItems.clear();
0879: if (cntSel != 0) {
0880: for (Iterator it = getItems().iterator(); it.hasNext();) {
0881: final Treeitem ti = (Treeitem) it.next();
0882: if (ti.isSelected()) {
0883: if (_sel == null)
0884: _sel = ti;
0885: _selItems.add(ti);
0886: if (--cntSel == 0)
0887: break;
0888: }
0889: }
0890: }
0891: }
0892:
0893: //-- Serializable --//
0894: private synchronized void readObject(java.io.ObjectInputStream s)
0895: throws java.io.IOException, ClassNotFoundException {
0896: s.defaultReadObject();
0897:
0898: init();
0899:
0900: afterUnmarshal(-1, -1);
0901: }
0902:
0903: //-- ComponentCtrl --//
0904: protected Object newExtraCtrl() {
0905: return new ExtraCtrl();
0906: }
0907:
0908: // TODO AREA JEFF ADDED
0909:
0910: private static final Log log = Log.lookup(Tree.class);
0911:
0912: private TreeModel _model;
0913:
0914: private TreeitemRenderer _renderer;
0915:
0916: private TreeDataListener _dataListener;
0917:
0918: /*
0919: * Handles when the tree model's content changed
0920: */
0921: private void onTreeDataChange(TreeDataEvent event) {
0922: //if the treepaht is empty, render tree's treechildren
0923: Object data = event.getParent();
0924: Component parent = getChildByNode(data);
0925: int indexFrom = event.getIndexFrom();
0926: int indexTo = event.getIndexTo();
0927: /*
0928: * Loop through indexes array
0929: * if INTERVAL_REMOVED, from end to beginning
0930: */
0931: switch (event.getType()) {
0932: case TreeDataEvent.INTERVAL_ADDED:
0933: for (int i = indexFrom; i <= indexTo; i++)
0934: onTreeDataInsert(parent, data, i);
0935: break;
0936: case TreeDataEvent.INTERVAL_REMOVED:
0937: for (int i = indexTo; i >= indexFrom; i--)
0938: onTreeDataRemoved(parent, data, i);
0939: break;
0940: case TreeDataEvent.CONTENTS_CHANGED:
0941: for (int i = indexFrom; i <= indexTo; i++)
0942: onTreeDataContentChanged(parent, data, i);
0943: break;
0944: }
0945:
0946: }
0947:
0948: private Treechildren getParentTreechildren(Object parent) {
0949: final Treechildren ch = (parent instanceof Tree) ? ((Tree) parent)
0950: .getTreechildren()
0951: : ((Treeitem) parent).getTreechildren();
0952: return (ch != null) ? ch : new Treechildren();
0953: }
0954:
0955: /*
0956: * Handle Treedata insertion
0957: */
0958: private void onTreeDataInsert(Component parent, Object node,
0959: int index) {
0960: /* Find the sibling to insertBefore;
0961: * if there is no sibling or new item is inserted at end.
0962: */
0963: Treeitem newTi = new Treeitem();
0964: Treechildren ch = getParentTreechildren(parent);
0965: renderItem(newTi, _model.getChild(node, index));
0966: List siblings = ch.getChildren();
0967: //if there is no sibling or new item is inserted at end.
0968: if (siblings.size() == 0 || index == siblings.size()) {
0969: ch.insertBefore(newTi, null);
0970: } else {
0971: ch.insertBefore(newTi, (Treeitem) siblings.get(index));
0972: }
0973: ch.setParent(parent);
0974: //if parent is Treeitem, setOpen
0975: if (parent instanceof Treeitem)
0976: ((Treeitem) parent).setOpen(true);
0977: }
0978:
0979: /*
0980: * Handle event that child is removed
0981: */
0982: private void onTreeDataRemoved(Component parent, Object node,
0983: int index) {
0984: List items = getParentTreechildren(parent).getChildren();
0985: if (items.size() > 1) {
0986: ((Treeitem) items.get(index)).detach();
0987: } else {
0988: getParentTreechildren(parent).detach();
0989: }
0990: //if parent is Treeitem, setOpen
0991: if (parent instanceof Treeitem)
0992: ((Treeitem) parent).setOpen(true);
0993: }
0994:
0995: /*
0996: * Handle event that child's content is changed
0997: */
0998: private void onTreeDataContentChanged(Component parent,
0999: Object node, int index) {
1000: List items = getParentTreechildren(parent).getChildren();
1001:
1002: /*
1003: * find the associated tree compoent(parent)
1004: * notice:
1005: * if parent is root
1006: */
1007: if (parent instanceof Tree)
1008: renderTree();
1009: else {
1010: /*
1011: * 2008/02/01 --- issue: [ 1884112 ] When Updating TreeModel, throws a IndexOutOfBoundsException
1012: * When I update a children node data of the TreeModel , and fire a
1013: * CONTENTS_CHANGED event, it will throw a IndexOutOfBoundsException , If a
1014: * node doesn't open yet or not load yet.
1015: *
1016: * if parent is loaded, change content.
1017: * else do nothing
1018: */
1019: if (items.size() > 0) {
1020: Treeitem ti = (Treeitem) items.get(index);
1021: /*
1022: * When content of treeitem is changed, the treeitem is rendered as
1023: * unloaded item.
1024: * 2007/11/05 --- issue: Can not dynamically update content of treeitem from treemodel
1025: */
1026: ti.setLoaded(false);
1027: renderItem(ti, _model.getChild(node, index));
1028: ti.setOpen(true);
1029: }
1030: }
1031: }
1032:
1033: /**
1034: * Return the Tree or Treeitem component by a given associated node in model.<br>
1035: * This implmentation calls {@link TreeModel#getPath} method to locate assoicated
1036: * Treeitem (or Tree) via path. You can override this method to speed up
1037: * performance if possible.
1038: * @since 3.0.0
1039: */
1040: protected Component getChildByNode(Object node) {
1041: int[] path = _model.getPath(_model.getRoot(), node);
1042:
1043: //If path is null or empty, return root(Tree)
1044: if (path == null || path.length == 0)
1045: return this ;
1046: else {
1047: Treeitem ti = (Treeitem) this .getTreechildren()
1048: .getChildren().get(path[0]);
1049: for (int i = 1; i < path.length; i++) {
1050: ti = (Treeitem) ti.getTreechildren().getChildren().get(
1051: path[i]);
1052: }
1053: return ti;
1054: }
1055: }
1056:
1057: /*
1058: * Initial Tree data listener
1059: */
1060: private void initDataListener() {
1061: if (_dataListener == null)
1062: _dataListener = new TreeDataListener() {
1063: public void onChange(TreeDataEvent event) {
1064: onTreeDataChange(event);
1065: }
1066: };
1067:
1068: _model.addTreeDataListener(_dataListener);
1069: }
1070:
1071: /** Sets the tree model associated with this tree.
1072: *
1073: * @param model the tree model to associate, or null to dis-associate
1074: * any previous model.
1075: * @exception UiException if failed to initialize with the model
1076: * @since 3.0.0
1077: */
1078: public void setModel(TreeModel model) throws Exception {
1079: _model = model;
1080: syncModel();
1081: initDataListener();
1082: }
1083:
1084: //--TreeModel dependent codes--//
1085: /** Returns the list model associated with this tree, or null
1086: * if this tree is not associated with any tree data model.
1087: * @return the list model associated with this tree
1088: * @since 3.0.0
1089: */
1090: public TreeModel getModel() {
1091: return _model;
1092: }
1093:
1094: /** Synchronizes the tree to be consistent with the specified model.
1095: */
1096: private void syncModel() throws Exception {
1097: if (_renderer == null)
1098: _renderer = getRealRenderer();
1099: renderTree();
1100: }
1101:
1102: /** Sets the renderer which is used to render each item
1103: * if {@link #getModel} is not null.
1104: *
1105: * <p>Note: changing a render will not cause the tree to re-render.
1106: * If you want it to re-render, you could assign the same model again
1107: * (i.e., setModel(getModel())), or fire an {@link TreeDataEvent} event.
1108: *
1109: * @param renderer the renderer, or null to use the default.
1110: * @exception UiException if failed to initialize with the model
1111: * @since 3.0.0
1112: */
1113: public void setTreeitemRenderer(TreeitemRenderer renderer) {
1114: _renderer = renderer;
1115: }
1116:
1117: /** Returns the renderer to render each item, or null if the default
1118: * renderer is used.
1119: * @return the renderer to render each item, or null if the default
1120: * @since 3.0.0
1121: */
1122: public TreeitemRenderer getTreeitemRenderer() {
1123: return _renderer;
1124: }
1125:
1126: /*
1127: * Render the root of Tree
1128: * Notice: _model.getRoot() is mapped to Tree, not first Treeitem
1129: */
1130: private void renderTree() {
1131: if (_treechildren != null)
1132: _treechildren = null;
1133: Treechildren children = new Treechildren();
1134: children.setParent(this );
1135: Object node = _model.getRoot();
1136: int childCount = _model.getChildCount(node);
1137: for (int i = 0; i < childCount; i++) {
1138: renderTreeChild(node, i);
1139: }
1140: }
1141:
1142: /*
1143: * Helper method for renderTree
1144: */
1145: private void renderTreeChild(Object node, int index) {
1146: Treeitem ti = new Treeitem();
1147: Object data = _model.getChild(node, index);
1148: try {
1149: _renderer.render(ti, data);
1150: } catch (Throwable ex) {
1151: try {
1152: ti.setLabel(Exceptions.getMessage(ex));
1153: } catch (Throwable t) {
1154: log.error(t);
1155: }
1156: ti.setOpen(true);
1157: }
1158: if (!_model.isLeaf(data)) {
1159: Treechildren ch = new Treechildren();
1160: ch.setParent(ti);
1161: }
1162: ti.setParent(_treechildren);
1163: }
1164:
1165: private static final TreeitemRenderer getDefaultItemRenderer() {
1166: return _defRend;
1167: }
1168:
1169: private static final TreeitemRenderer _defRend = new TreeitemRenderer() {
1170: public void render(Treeitem ti, Object data) {
1171: Treecell tc = new Treecell(data.toString());
1172: Treerow tr = null;
1173: if (ti.getTreerow() == null) {
1174: tr = new Treerow();
1175: tr.setParent(ti);
1176: } else {
1177: tr = ti.getTreerow();
1178: tr.getChildren().clear();
1179: }
1180: tc.setParent(tr);
1181: ti.setOpen(false);
1182: }
1183: };
1184:
1185: /** Returns the renderer used to render items.
1186: */
1187: private TreeitemRenderer getRealRenderer() {
1188: return _renderer != null ? _renderer : getDefaultItemRenderer();
1189: }
1190:
1191: /** Used to render treeitem if _model is specified. */
1192: private class Renderer implements java.io.Serializable {
1193: private final TreeitemRenderer _renderer;
1194: private boolean _rendered, _ctrled;
1195:
1196: private Renderer() {
1197: _renderer = getRealRenderer();
1198: }
1199:
1200: private void render(Treeitem item) throws Throwable {
1201: if (!item.isOpen())
1202: return; //nothing to do
1203: if (!_rendered && (_renderer instanceof RendererCtrl)) {
1204: ((RendererCtrl) _renderer).doTry();
1205: _ctrled = true;
1206: }
1207:
1208: try {
1209: Object node = getAssociatedNode(item, Tree.this );
1210: _renderer.render(item, node);
1211: } catch (Throwable ex) {
1212: try {
1213: item.setLabel(Exceptions.getMessage(ex));
1214: } catch (Throwable t) {
1215: log.error(t);
1216: }
1217: item.setOpen(true);
1218: throw ex;
1219: }
1220:
1221: item.setOpen(true);
1222: _rendered = true;
1223: }
1224:
1225: private void doCatch(Throwable ex) {
1226: if (_ctrled) {
1227: try {
1228: ((RendererCtrl) _renderer).doCatch(ex);
1229: } catch (Throwable t) {
1230: throw UiException.Aide.wrap(t);
1231: }
1232: } else {
1233: throw UiException.Aide.wrap(ex);
1234: }
1235: }
1236:
1237: private void doFinally() {
1238: if (_ctrled)
1239: ((RendererCtrl) _renderer).doFinally();
1240: }
1241: }
1242:
1243: /** Renders the specified {@link Treeitem} if not loaded yet,
1244: * with {@link #getTreeitemRenderer}.
1245: *
1246: * <p>It does nothing if {@link #getModel} returns null.
1247: *
1248: * @see #renderItems
1249: * @since 3.0.0
1250: */
1251: public void renderItem(Treeitem item) {
1252: if (_model == null)
1253: return;
1254: final Renderer renderer = new Renderer();
1255: try {
1256: renderItem(item, getAssociatedNode(item, this ));
1257: } catch (Throwable ex) {
1258: renderer.doCatch(ex);
1259: } finally {
1260: renderer.doFinally();
1261: }
1262: }
1263:
1264: /** Renders the specified {@link Treeitem} with data if not loaded yet,
1265: * with {@link #getTreeitemRenderer}.
1266: *
1267: * <p>It does nothing if {@link #getModel} returns null.
1268: *
1269: *<p>Note: Since the corresponding data is given,
1270: * This method has better performance than
1271: * renderItem(Treeitem item) due to not searching for its
1272: * corresponding data.
1273: * @see #renderItems
1274: * @since 3.0.0
1275: *
1276: *
1277: */
1278: public void renderItem(Treeitem item, Object data) {
1279: if (_model == null)
1280: return;
1281: final Renderer renderer = new Renderer();
1282: try {
1283: dfRenderItem(data, item);
1284: } catch (Throwable ex) {
1285: renderer.doCatch(ex);
1286: } finally {
1287: renderer.doFinally();
1288: }
1289: }
1290:
1291: /**
1292: * Render the treetiem with given node and its children
1293: */
1294: private void dfRenderItem(Object node, Treeitem item)
1295: throws Exception {
1296: //if treeitem is not loaded, load it
1297: if (!item.isLoaded()) {
1298: Treechildren children = null;
1299:
1300: if (item.getTreechildren() != null) {
1301: children = item.getTreechildren();
1302: /*
1303: * When the treeitem is rendered after 1st time, dropped all
1304: * the descending treeitems first.
1305: */
1306: if (children.getItemCount() > 0)
1307: children.getChildren().clear();
1308: } else {
1309: children = new Treechildren();
1310: _renderer.render(item, node);
1311: }
1312: /*
1313: * After modified the node in tree model, if node is leaf,
1314: * its treechildren is needed to be dropped.
1315: */
1316: if (_model.isLeaf(node)) {
1317: _renderer.render(item, node);
1318: if (item.getTreechildren() != null)
1319: item.getTreechildren().detach();
1320: } else {
1321: /*
1322: * render children of item
1323: */
1324: for (int i = 0; i < _model.getChildCount(node); i++) {
1325: Treeitem ti = new Treeitem();
1326: Object data = _model.getChild(node, i);
1327: _renderer.render(ti, data);
1328: if (!_model.isLeaf(data)) {
1329: Treechildren ch = new Treechildren();
1330: ch.setParent(ti);
1331: }
1332: ti.setParent(children);
1333: }
1334: children.setParent(item);
1335: }
1336: //After the treeitem is loaded with data, set treeitem to be loaded
1337: item.setLoaded(true);
1338: }
1339: }
1340:
1341: /** Renders the specified {@link Treeitem}s with data if not loaded yet,
1342: * with {@link #getTreeitemRenderer}.
1343: *
1344: * <p>It does nothing if {@link #getModel} returns null.
1345: *
1346: * @see #renderItem
1347: * @since 3.0.0
1348: */
1349: public void renderItems(Set items) {
1350: if (_model == null)
1351: return;
1352:
1353: if (items.isEmpty())
1354: return; //nothing to do
1355:
1356: final Renderer renderer = new Renderer();
1357: try {
1358: for (Iterator it = items.iterator(); it.hasNext();) {
1359: Treeitem item = (Treeitem) it.next();
1360: Object data = getAssociatedNode(item, this );
1361: dfRenderItem(data, item);
1362: }
1363: } catch (Throwable ex) {
1364: renderer.doCatch(ex);
1365: } finally {
1366: renderer.doFinally();
1367: }
1368: }
1369:
1370: /**
1371: * Return a node which is an associated Treeitem ti in a Tree tree
1372: * @since 3.0.0
1373: */
1374: protected Object getAssociatedNode(Treeitem ti, Tree t) {
1375: return getNodeByPath(getTreeitemPath(t, ti), _model.getRoot());
1376: }
1377:
1378: /**
1379: * return the path which is from ZK Component root to ZK Component lastNode
1380: */
1381: private List getTreeitemPath(Component root, Component lastNode) {
1382: List al = new ArrayList();
1383: Component curNode = lastNode;
1384: while (!root.equals(curNode)) {
1385: if (curNode instanceof Treeitem) {
1386: al.add(new Integer(((Treeitem) curNode).indexOf()));
1387: }
1388: curNode = curNode.getParent();
1389: }
1390: return al;
1391: }
1392:
1393: /**
1394: * Get the node from tree by given path
1395: * @param path
1396: * @param root
1397: * @return the node from tree by given path
1398: * @since 3.0.0
1399: */
1400: private Object getNodeByPath(List path, Object root) {
1401: Object node = root;
1402: int pathSize = path.size() - 1;
1403: for (int i = pathSize; i >= 0; i--) {
1404: node = _model.getChild(node, ((Integer) (path.get(i)))
1405: .intValue());
1406: }
1407: return node;
1408: }
1409:
1410: /**
1411: * Load treeitems through path <b>path</b>
1412: * <br>Note: By using this method, all treeitems in path will be rendered
1413: * @param path - an int[] path, see {@link TreeModel#getPath}
1414: * @return the treeitem from tree by given path
1415: * @since 3.0.0
1416: */
1417: public Treeitem renderItemByPath(int[] path) {
1418: if (path == null || path.length == 0)
1419: return null;
1420: //Start from root-Tree
1421: Treeitem ti = null;
1422: List children = this .getTreechildren().getChildren();
1423: /*
1424: * Go through each stop in path and render corresponding treeitem
1425: */
1426: for (int i = 0; i < path.length; i++) {
1427: if (path[i] < 0 || path[i] > children.size())
1428: return null;
1429: ti = (Treeitem) children.get(path[i]);
1430: renderItem(ti);
1431: if (i < path.length - 1)
1432: ti.setOpen(true);
1433: if (ti.getTreechildren() != null) {
1434: children = ti.getTreechildren().getChildren();
1435: } else {
1436: if (i != path.length - 1) {
1437: return null;
1438: }
1439: }
1440: }
1441: return ti;
1442: }
1443:
1444: //TODO AREA JEFF ADDED END
1445:
1446: /** A utility class to implement {@link #getExtraCtrl}.
1447: * It is used only by component developers.
1448: */
1449:
1450: protected class ExtraCtrl extends XulElement.ExtraCtrl implements
1451: InnerWidth, Selectable {
1452: //InnerWidth//
1453: public void setInnerWidthByClient(String width) {
1454: _innerWidth = width == null ? "100%" : width;
1455: }
1456:
1457: //-- Selectable --//
1458: public void selectItemsByClient(Set selItems) {
1459: _noSmartUpdate = true;
1460: try {
1461: if (!_multiple || selItems == null
1462: || selItems.size() <= 1) {
1463: final Treeitem item = selItems != null
1464: && selItems.size() > 0 ? (Treeitem) selItems
1465: .iterator().next()
1466: : null;
1467: selectItem(item);
1468: } else {
1469: for (Iterator it = new ArrayList(_selItems)
1470: .iterator(); it.hasNext();) {
1471: final Treeitem item = (Treeitem) it.next();
1472: if (!selItems.remove(item))
1473: removeItemFromSelection(item);
1474: }
1475: for (Iterator it = selItems.iterator(); it
1476: .hasNext();)
1477: addItemToSelection((Treeitem) it.next());
1478: }
1479: } finally {
1480: _noSmartUpdate = false;
1481: }
1482: }
1483: }
1484:
1485: /** An iterator used by _heads.
1486: */
1487: private class Iter implements Iterator {
1488: private final ListIterator _it = getChildren().listIterator();
1489:
1490: public boolean hasNext() {
1491: while (_it.hasNext()) {
1492: Object o = _it.next();
1493: if (o instanceof Treecols || o instanceof Auxhead) {
1494: _it.previous();
1495: return true;
1496: }
1497: }
1498: return false;
1499: }
1500:
1501: public Object next() {
1502: for (;;) {
1503: Object o = _it.next();
1504: if (o instanceof Treecols || o instanceof Auxhead)
1505: return o;
1506: }
1507: }
1508:
1509: public void remove() {
1510: throw new UnsupportedOperationException();
1511: }
1512: }
1513: }
|