📄 listbox.java
字号:
/* Listbox.java{{IS_NOTE Purpose: Description: History: Wed Jun 15 17:25:00 2005, Created by tomyeh}}IS_NOTECopyright (C) 2005 Potix Corporation. All Rights Reserved.{{IS_RIGHT This program is distributed under GPL Version 2.0 in the hope that it will be useful, but WITHOUT ANY WARRANTY.}}IS_RIGHT*/package org.zkoss.zul;import java.util.List;import java.util.AbstractSequentialList;import java.util.LinkedList;import java.util.ArrayList;import java.util.Set;import java.util.HashSet;import java.util.LinkedHashSet;import java.util.Iterator;import java.util.ListIterator;import java.util.Collection;import java.util.Collections;import java.util.AbstractCollection;import java.util.NoSuchElementException;import org.zkoss.lang.D;import org.zkoss.lang.Classes;import org.zkoss.lang.Objects;import org.zkoss.lang.Strings;import org.zkoss.lang.Exceptions;import org.zkoss.util.logging.Log;import org.zkoss.xml.HTMLs;import org.zkoss.zk.ui.Page;import org.zkoss.zk.ui.Component;import org.zkoss.zk.ui.UiException;import org.zkoss.zk.ui.WrongValueException;import org.zkoss.zk.ui.ext.client.RenderOnDemand;import org.zkoss.zk.ui.ext.client.Selectable;import org.zkoss.zk.ui.ext.client.InnerWidth;import org.zkoss.zk.ui.ext.render.Cropper;import org.zkoss.zk.ui.event.Event;import org.zkoss.zk.ui.event.EventListener;import org.zkoss.zk.ui.event.Events;import org.zkoss.zul.impl.XulElement;import org.zkoss.zul.event.ListDataEvent;import org.zkoss.zul.event.ListDataListener;import org.zkoss.zul.ext.Paginal;import org.zkoss.zul.event.ZulEvents;import org.zkoss.zul.event.PagingEvent;/** * A listbox. * * <p>Event: * <ol> * <li>org.zkoss.zk.ui.event.SelectEvent is sent when user changes * the selection.</li> * </ol> * * <p>See <a href="package-summary.html">Specification</a>.</p> * * <p>Besides creating {@link Listitem} programmingly, you could assign * a data model (a {@link ListModel} instance) to a listbox * via {@link #setModel} and then * the listbox will retrieve data via {@link ListModel#getElementAt} when * necessary. * * <p>Besides assign a list model, you could assign a renderer * (a {@link ListitemRenderer} instance) to a listbox, such that * the listbox will use this * renderer to render the data returned by {@link ListModel#getElementAt}. * If not assigned, the default renderer, which assumes a label per * list item, is used. * In other words, the default renderer adds a label to * a row by calling toString against the object returned * by {@link ListModel#getElementAt} * * <p>There are two ways to handle long content: scrolling and paging. * If {@link #getMold} is "default", scrolling is used if {@link #setHeight} * is called and too much content to display. * If {@link #getMold} is "paging", paging is used if two or more pages are * required. To control the number of items to display in a page, use * {@link #setPageSize}. * * <p>If paging is used, the page controller is either created automatically * or assigned explicity by {@link #setPaginal}. * The paging controller specified explicitly by {@link #setPaginal} is called * the external page controller. It is useful if you want to put the paging * controller at different location (other than as a child component), or * you want to use the same controller to control multiple listboxes. * * <p>Default {@link #getSclass}: listbox. * * <p>To have a list box without stripping, you can specify a non-existent * style class to {@link #setOddRowSclass}. * If you want to disable all striping, you can specify the style: * <pre><code> tr.odd { background: white; } * </code></pre> * * @author tomyeh * @see ListModel * @see ListitemRenderer * @see ListitemRendererExt */public class Listbox extends XulElement { private static final Log log = Log.lookup(Listbox.class); private transient List _items; /** A list of selected items. */ private transient Set _selItems; /** A readonly copy of {@link #_selItems}. */ private transient Set _roSelItems; private int _maxlength; private int _rows, _jsel = -1; private transient Listhead _listhead; private transient Listfoot _listfoot; private ListModel _model; private ListitemRenderer _renderer; private transient ListDataListener _dataListener; private transient Collection _heads; private int _hdcnt; private String _innerWidth = "100%"; /** The name. */ private String _name; /** The paging controller, used only if mold = "paging". */ private transient Paginal _pgi; /** The paging controller, used only if mold = "paging" and user * doesn't assign a controller via {@link #setPaginal}. * If exists, it is the last child */ private transient Paging _paging; private transient EventListener _pgListener, _pgImpListener; /** The style class of the odd row. */ private String _scOddRow = "odd"; private int _tabindex = -1; /** the # of rows to preload. */ private int _preloadsz = 7; private boolean _multiple; private boolean _disabled, _checkmark; private boolean _vflex; /** disable smartUpdate; usually caused by the client. */ private boolean _noSmartUpdate; public Listbox() { setSclass("listbox"); init(); } private void init() { _items = new AbstractSequentialList () { public ListIterator listIterator(int index) { return new ItemIter(index); } public Object get(int j) { final Object o = Listbox.this.getChildren().get(j + _hdcnt); if (!(o instanceof Listitem)) throw new IndexOutOfBoundsException("Wrong index: "+j); return o; } public int size() { int sz = getChildren().size() - _hdcnt; if (_listfoot != null) --sz; if (_paging != null) --sz; return sz; } }; _selItems = new LinkedHashSet(5); _roSelItems = Collections.unmodifiableSet(_selItems); _heads = new AbstractCollection() { public int size() { return _hdcnt; } public Iterator iterator() { return new Iter(); } }; } /** Initializes _dataListener and register the listener to the model */ private void initDataListener() { if (_dataListener == null) _dataListener = new ListDataListener() { public void onChange(ListDataEvent event) { onListDataChange(event); } }; _model.addListDataListener(_dataListener); } /** Returns {@link Listhead} belonging to this listbox, or null * if no list headers at all. */ public Listhead getListhead() { return _listhead; } /** Returns {@link Listfoot} belonging to this listbox, or null * if no list footers at all. */ public Listfoot getListfoot() { return _listfoot; } /** Returns a collection of heads, including {@link #getListhead} * and auxiliary heads ({@link Auxhead}) (never null). * * @since 3.0.0 */ public Collection getHeads() { return _heads; } /** Returns whether the HTML's select tag is used. */ /*package*/ final boolean inSelectMold() { return "select".equals(getMold()); } /** Returns whether the check mark shall be displayed in front * of each item. * <p>Default: false. */ public final boolean isCheckmark() { return _checkmark; } /** Sets whether the check mark shall be displayed in front * of each item. * <p>The check mark is a checkbox if {@link #isMultiple} returns * true. It is a radio button if {@link #isMultiple} returns false. */ public void setCheckmark(boolean checkmark) { if (_checkmark != checkmark) { _checkmark = checkmark; if (!inSelectMold()) invalidate(); } } /** * Sets the inner width of this component. * The inner width is the width of the inner table. * By default, it is 100%. That is, it is the same as the width * of this component. However, it is changed when the user * is sizing the column's width. * * <p>Application developers rarely call this method, unless * they want to preserve the widths of sizable columns * changed by the user. * To preserve the widths, the developer have to store the widths of * all columns and the inner width ({@link #getInnerWidth}), * and then restore them when re-creating this component. * * @param innerWidth the inner width. If null, "100%" is assumed. * @since 3.0.0 */ public void setInnerWidth(String innerWidth) { if (innerWidth == null) innerWidth = "100%"; if (!_innerWidth.equals(innerWidth)) { _innerWidth = innerWidth; smartUpdate("z.innerWidth", innerWidth); } } /** * Returns the inner width of this component. * The inner width is the width of the inner table. * <p>Default: "100%" * @see #setInnerWidth * @since 3.0.0 */ public String getInnerWidth() { return _innerWidth; } /** Returns whether to grow and shrink vertical to fit their given space, * so called vertial flexibility. * * <p>Note: this attribute is ignored if {@link #setRows} is specified * * <p>Default: false. */ public final boolean isVflex() { return _vflex; } /** Sets whether to grow and shrink vertical to fit their given space, * so called vertial flexibility. * * <p>Note: this attribute is ignored if {@link #setRows} is specified */ public void setVflex(boolean vflex) { if (_vflex != vflex) { _vflex = vflex; if (!inSelectMold()) smartUpdate("z.flex", _vflex); } } /** Returns whether it is disabled. * <p>Default: false. */ public final boolean isDisabled() { return _disabled; } /** Sets whether it is disabled. */ public void setDisabled(boolean disabled) { if (_disabled != disabled) { _disabled = disabled; if (inSelectMold()) { smartUpdate("disabled", _disabled); } else { smartUpdate("z.disabled", _disabled); } } } /** Returns the tab order of this component. * <p>Currently, only the "select" mold supports this property. * <p>Default: -1 (means the same as browser's default). */ public int getTabindex() { return _tabindex; } /** Sets the tab order of this component. * <p>Currently, only the "select" mold supports this property. */ public void setTabindex(int tabindex) throws WrongValueException { if (_tabindex != tabindex) { _tabindex = tabindex; if (tabindex < 0) smartUpdate("tabindex", null); else smartUpdate("tabindex", Integer.toString(_tabindex)); } } /** Returns the rows. Zero means no limitation. * <p>Default: 0. */ public int getRows() { return _rows; } /** Sets the rows. * <p>Note: if both {@link #setHeight} is specified with non-empty, * {@link #setRows} is ignored */ public void setRows(int rows) throws WrongValueException { if (rows < 0) throw new WrongValueException("Illegal rows: "+rows); if (_rows != rows) { _rows = rows; if (inSelectMold()) { smartUpdate("size", _rows > 0 ? Integer.toString(_rows): null); } else { smartUpdate("z.size", Integer.toString(_rows)); } } } /** Returns the seltype. * <p>Default: "single". */ public String getSeltype() { return _multiple ? "multiple": "single"; } /** Sets the seltype. */ public void setSeltype(String seltype) throws WrongValueException { if ("single".equals(seltype)) setMultiple(false); else if ("multiple".equals(seltype)) setMultiple(true); else throw new WrongValueException("Unknown seltype: "+seltype); } /** Returns whether multiple selections are allowed. * <p>Default: false. */ public boolean isMultiple() { return _multiple; } /** Sets whether multiple selections are allowed. */ public void setMultiple(boolean multiple) { if (_multiple != multiple) { _multiple = multiple; if (!_multiple && _selItems.size() > 1) { final Listitem item = getSelectedItem(); for (Iterator it = _selItems.iterator(); it.hasNext();) { final Listitem li = (Listitem)it.next(); if (li != item) { li.setSelectedDirectly(false); it.remove(); } } //No need to update z.selId because z.multiple will do the job } if (inSelectMold()) smartUpdate("multiple", _multiple); else if (isCheckmark()) invalidate(); //change check mark else smartUpdate("z.multiple", _multiple); //No need to use response because such info is carried on tags } } /** Returns the ID of the selected item (it is stored as the z.selId * attribute of the listbox). */ private String getSelectedId() { final Listitem sel = getSelectedItem(); return sel != null ? sel.getUuid(): "zk_n_a"; } /** Returns the maximal length of each item's label. */ public int getMaxlength() { return _maxlength; } /** Sets the maximal length of each item's label. */ public void setMaxlength(int maxlength) { if (maxlength < 0) maxlength = 0; if (_maxlength != maxlength) { _maxlength = maxlength; if (inSelectMold()) //affects only the HTML-select listbox invalidate(); //Both IE and Mozilla are buggy if we insert options by innerHTML } } /** Returns the name of this component. * <p>Default: null. * <p>The name is used only to work with "legacy" Web application that * handles user's request by servlets. * It works only with HTTP/HTML-based browsers. It doesn't work * with other kind of clients. * <p>Don't use this method if your application is purely based * on ZK's event-driven model. */ public String getName() { return _name; } /** Sets the name of this component. * <p>The name is used only to work with "legacy" Web application that * handles user's request by servlets. * It works only with HTTP/HTML-based browsers. It doesn't work * with other kind of clients. * <p>Don't use this method if your application is purely based * on ZK's event-driven model. * * @param name the name of this component. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -