📄 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.Collections;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.RenderOnDemand;import org.zkoss.zk.ui.ext.client.Selectable;import org.zkoss.zk.ui.ext.render.ChildChangedAware;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, and then it will use this * renderer to render the data 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. * * @author tomyeh * @see ListModel * @see ListitemRenderer */public class Listbox extends XulElementimplements java.io.Serializable, RenderOnDemand { private static final Log log = Log.lookup(Listbox.class); private transient List _items; /** A list of selected items. */ private transient Set _selItems; 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; /** 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; private boolean _multiple; private boolean _disabled, _readonly, _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(_listhead != null ? j + 1: j); if (!(o instanceof Listitem)) throw new IndexOutOfBoundsException("Wrong index: "+j); return o; } public int size() { int sz = getChildren().size(); if (_listhead != null) --sz; if (_listfoot != null) --sz; if (_paging != null) --sz; return sz; } }; _selItems = new LinkedHashSet(5); } private void initDataListener() { if (_dataListener == null) _dataListener = new ListDataListener() { public void onChange(ListDataEvent event) { onListDataChange(event); } }; } /** 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 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(); } } /** 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 whether it is readonly. * <p>Default: false. */ public final boolean isReadonly() { return _readonly; } /** Sets whether it is readonly. */ public void setReadonly(boolean readonly) { if (_readonly != readonly) { _readonly = readonly; if (inSelectMold()) { smartUpdate("readonly", _readonly); } else { smartUpdate("z.readonly", _readonly); } } } /** 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)); initAtClient(); //Don't use smartUpdate because client has to extra job //besides maintaining HTML DOM } } } /** 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(); _selItems.clear(); if (item != null) _selItems.add(item); //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>Don't use this method if your application is purely based * on ZK's event-driven model. * <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. */ public String getName() { return _name; } /** Sets the name of this component. * <p>Don't use this method if your application is purely based * on ZK's event-driven model. * <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. * * @param name the name of this component. */ public void setName(String name) { if (name != null && name.length() == 0) name = null; if (!Objects.equals(_name, name)) { if (inSelectMold()) smartUpdate("name", _name); else if (_name != null) smartUpdate("z.name", _name); else invalidate(); //1) generate _value; 2) add submit listener _name = name; } } /** Returns a live list of all {@link Listitem}. * By live we mean you can add or remove them directly with * the List interface. In other words, you could add or remove * an item by manipulating the returned list directly. */ public List getItems() { return _items; } /** Returns the number of items. */ public int getItemCount() { return _items.size(); } /** Returns the item at the specified index. */ public Listitem getItemAtIndex(int index) { return (Listitem)_items.get(index); } /** Returns the index of the specified item, or -1 if not found. */ public int getIndexOfItem(Listitem item) { return item == null ? -1: item.getIndex(); } /** Returns the index of the selected item (-1 if no one is selected). */ public int getSelectedIndex() { return _jsel; } /** Deselects all of the currently selected items and selects * the item with the given index. */ public void setSelectedIndex(int jsel) { if (jsel >= _items.size()) throw new UiException("Out of bound: "+jsel+" while size="+_items.size()); if (jsel < -1) jsel = -1; if (jsel < 0) { //unselct all clearSelection(); } else if (jsel != _jsel || (_multiple && _selItems.size() > 1)) { for (Iterator it = _selItems.iterator(); it.hasNext();) { final Listitem item = (Listitem)it.next(); item.setSelectedDirectly(false); } _selItems.clear(); _jsel = jsel; final Listitem item = getItemAtIndex(_jsel); item.setSelectedDirectly(true); _selItems.add(item); smartUpdate("selectedIndex", Integer.toString(_jsel)); //No need to use response because such info is carried on tags } } /** Deselects all of the currently selected items and selects * the given item. * <p>It is the same as {@link #setSelectedItem}. * @param item the item to select. If null, all items are deselected. */ public void selectItem(Listitem item) { if (item == null) { setSelectedIndex(-1); } else { if (item.getParent() != this) throw new UiException("Not a child: "+item); if (_multiple || !item.isSelected()) setSelectedIndex(item.getIndex()); } } /** Selects the given item, without deselecting any other items * that are already selected.. */ public void addItemToSelection(Listitem item) { if (item.getParent() != this) throw new UiException("Not a child: "+item); if (!item.isSelected()) { if (!_multiple) { selectItem(item); } else { if (item.getIndex() < _jsel || _jsel < 0) { _jsel = item.getIndex(); if (!inSelectMold()) smartUpdate("z.selId", getSelectedId()); } item.setSelectedDirectly(true); _selItems.add(item); if (inSelectMold()) { item.smartUpdate("selected", true); } else { smartUpdateSelection(); } } } } /** Deselects the given item without deselecting other items. */ public void removeItemFromSelection(Listitem item) { if (item.getParent() != this) throw new UiException("Not a child: "+item); if (item.isSelected()) { if (!_multiple) { clearSelection(); } else { final int oldSel = _jsel; item.setSelectedDirectly(false); _selItems.remove(item); fixSelectedIndex(0); if (inSelectMold()) { item.smartUpdate("selected", false); } else { smartUpdateSelection(); if (oldSel != _jsel) smartUpdate("z.selId", getSelectedId()); } } } } /** Note: we have to update all selection at once, since addItemToSelection
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -