> iterable = p.getEventListeners(Events.ON_OK);
if (iterable.iterator().hasNext()) {
Events.postEvent(Events.ON_OK, p, null);
break;
} else {
p = p.getParent();
}
}
}
public String getZclass() {
return _zclass == null ? "z-chosenbox" : _zclass;
}
public void setOpen(boolean open) {
if (_open != open) {
_open = open;
smartUpdate("open", _open);
}
}
public boolean isOpen() {
return _open;
}
/**
* Returns the tab order of the input node of this component.
*
* Default: 0 (means the same as browser's default).
*/
public int getTabindex() {
return _tabindex;
}
/**
* Sets the tab order of the input node of this component.
* @param tabindex
*/
public void setTabindex(int tabindex) throws WrongValueException {
if (_tabindex != tabindex) {
_tabindex = tabindex;
smartUpdate("tabindex", tabindex);
}
}
/**
* Returns whether it is disabled.
*
* Default: false.
*/
public boolean isDisabled() {
return _disabled;
}
/**
* Sets whether it is disabled.
*/
public void setDisabled(boolean disabled) {
if (_disabled != disabled) {
_disabled = disabled;
smartUpdate("disabled", _disabled);
}
}
/**
* Returns the name of this component.
*
* Default: null.
*
* 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.
*
* 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 the input element of this component.
*
* 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.
*
* Don't use this method if your application is purely based on ZK's
* event-driven model.
*
* @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)) {
_name = name;
smartUpdate("name", name);
}
}
/**
* Returns the emptyMessage of the input of this component.
*
* Default: null.
*
* The emptyMessage will be displayed in input if nothing selected and not focused.
* @return String
*/
public String getEmptyMessage() {
return _emptyMessage;
}
/**
* Sets the emptyMessage of the input of this component.
*
* The emptyMessage will be displayed in input if nothing selected and not focused.
* @param String emptyMessage
* the emptyMessage of the input of this component.
*/
public void setEmptyMessage(String emptyMessage) {
if (emptyMessage != null && emptyMessage.length() == 0)
emptyMessage = null;
if (!Objects.equals(_emptyMessage, emptyMessage)) {
_emptyMessage = emptyMessage;
smartUpdate("emptyMessage", getEmptyMessage());
}
}
/**
* Returns the no-result text of this component.
*
* Default: null.
*
* The no-result text will be displayed in popup if nothing match to the input value and can not create either,
* the syntax "{0}" will be replaced with the input value at client side.
* @return String
*/
public String getNoResultsText() {
return _noResultsText;
}
/**
* Sets the no-result text of this component.
*
* The no-result text will be displayed in popup if nothing match to the input value and can not create either,
* the syntax "{0}" will be replaced with the input value at client side.
* @param String noResultsText
* the no-result text of this component.
*/
public void setNoResultsText(String noResultsText) {
if (noResultsText != null && noResultsText.length() == 0)
noResultsText = null;
if (!Objects.equals(_noResultsText, noResultsText)) {
_noResultsText = noResultsText;
smartUpdate("noResultsText", getNoResultsText());
}
}
/**
* Returns the create message of this component.
*
* Default: null.
*
* The create message will be displayed in popup if nothing match to the input value but can create as new label,
* the syntax "{0}" will be replaced with the input value at client side.
* @return String
*/
public String getCreateMessage() {
return _createMessage;
}
/**
* Sets the create message of this component.
*
* The create message will be displayed in popup if nothing match to the input value but can create as new label,
* the syntax "{0}" will be replaced with the input value at client side.
* @param String createMessage
* the create message of this component.
*/
public void setCreateMessage(String createMessage) {
if (createMessage != null && createMessage.length() == 0)
createMessage = null;
if (!Objects.equals(_createMessage, createMessage)) {
_createMessage = createMessage;
smartUpdate("createMessage", getCreateMessage());
}
}
/**
* Returns the separate chars of this component.
*
* Support: 0-9, A-Z (case insensitive), and ,.;'[]/\-=
*
* Default: null.
*
* The separate chars will work as 'Enter' key,
* it will not considered as input value but send onSerch or onSearching while key up.
* @return String
*/
public String getSeparator() {
return _separator;
}
/**
* Sets the separate chars of this component.
*
* Support: 0-9, A-Z (case insensitive), and ,.;'[]/\-=
*
* The separate chars will work as 'Enter' key,
* it will not considered as input value but send onSerch or onSelect while key up.
* @param String createMessage
* the create message of this component.
*/
public void setSeparator(String separator) {
if (separator != null && separator.length() == 0)
separator = null;
if (!Objects.equals(_separator, separator)) {
_separator = separator;
smartUpdate("separator", getSeparator());
}
}
/**
* Returns the selected objects.
* @return Set
*/
public LinkedHashSet getSelectedObjects () {
final LinkedHashSet objects = new LinkedHashSet<>();
ListModel model = this.getModel();
if (model != null) {
for (int i = 0; i < _selIdxs.size(); i ++) {
objects.add(model.getElementAt(_selIdxs.get(i)));
}
}
return objects;
}
/**
* Sets the selected objects.
* It will clear selection first.
* @param List objects
* the objects to select.
*/
public void setSelectedObjects (Set objects) {
// do nothing if no model
if (getModel() != null) {
_selIdxs.clear();
ListModel lm = getModel();
boolean found = false;
for (T object : objects) {
for (int i = 0; i < lm.getSize(); i++) {
if (lm.getElementAt(i).equals(object)) {
if (_jsel == -1 || _jsel > i)
_jsel = i;
found = true;
_selIdxs.add(i);
break;
}
}
if (!found) {
if (Logger.getLogger(getClass().getName()).isLoggable(Level.INFO))
Logger.getLogger(getClass().getName()).info("No such item: " + object);
}
found = false;
}
smartUpdate("chgSel", getChgSel());
}
}
/**
* Returns the index of the selected item (-1 if no one is selected).
* @return int
*/
public int getSelectedIndex() {
return _jsel;
}
/**
* Sets the index of the selected item (-1 if no one is selected).
* It will clear selection first.
* @param int index
* the index to select.
*/
public void setSelectedIndex(int jsel) {
if (jsel <= -1)
jsel = -1;
if (jsel < 0) { // unselect all
clearSelection();
} else if (jsel != _jsel || _selIdxs.size() > 1) {
if (_selIdxs.size() > 1 && jsel == _jsel) {
// clear client side old value
smartUpdate("selectedIndex", -1);
}
// check size
if (getModel() != null && jsel >= getModel().getSize()) {
throw new UiException("Out of bound: " + jsel + " while size="
+ getModel().getSize());
}
_selIdxs.clear();
_jsel = jsel;
_selIdxs.add(jsel);
smartUpdate("chgSel", getChgSel());
}
}
/**
* Returns whether can create new item.
*
* Default: false.
*
* true: will show create message while value of input not exists.
*
* false: will show no result message while value of input not exists.
*/
public boolean isCreatable() {
return _creatable;
}
/**
* Sets whether can create new item.
*
* Default: false.
*
* true: will show create message while value of input not exists.
*
* false: will show no-result text while value of input not exists.
*
* @param creatable
* the boolean value.
*/
public void setCreatable(boolean creatable) {
if (_creatable != creatable) {
_creatable = creatable;
smartUpdate("creatable", _creatable);
}
}
public ItemRenderer getRealRenderer() {
final ItemRenderer renderer = getItemRenderer();
return renderer != null ? renderer : _defRend;
}
/**
* Returns the renderer to render each item, or null if the default renderer
* is used.
*/
public ItemRenderer getItemRenderer() {
return _renderer;
}
/**
* Returns the model associated with this chosenbox, or null if this
* chosenbox is not associated with any list data model.
*/
public ListModel getModel() {
return _model;
}
/**
* Sets the list model associated with this chosenbox. If a non-null model
* is assigned, no matter whether it is the same as the previous, it will
* always cause re-render.
*
* @param model
* the list model to associate, or null to dis-associate any
* previous model.
* @exception UiException
* if failed to initialize with the model
*/
public void setModel(ListModelList model) {
if (model != null) {
if (_model != model) {
// fix selected index
if (getSelectedIndex() >= model.getSize())
setSelectedIndex(model.getSize()-1);
if (_model != null) {
_model.removeListDataListener(_dataListener);
}
_model = model;
initDataListener();
}
} else if (_model != null) {
_model.removeListDataListener(_dataListener);
if (_model instanceof ListSubModel)
removeEventListener("onSearching", _eventListener);
_model = null;
}
fixIndexs(true, null);
smartUpdate("renderByServer", _model instanceof ListSubModel);
updateItems();
}
/**
* Clear all selected objects.
*/
public void clearSelection() {
_selIdxs.clear();
_jsel = -1;
smartUpdate("chgSel", getChgSel());
}
/**
* Add an item into selection.
* @param o
* the object to add.
*/
public void addItemToSelection(Object o) {
// do nothing if no model
if (getModel() != null) {
ListModel lm = getModel();
for (int i = 0;i < lm.getSize();i ++) {
if (lm.getElementAt(i).equals(o)) {
_selIdxs.add(i);
if (i < _jsel)
_jsel = i;
smartUpdate("chgSel", getChgSel());
}
}
}
}
/**
* Remove an item from selection.
* @param o
* the object to remove.
*/
public void removeItemFromSelection(Object o) {
// do nothing if no model
if (getModel() != null) {
ListModel lm = getModel();
for (int i = 0;i < lm.getSize();i ++) {
if (lm.getElementAt(i).equals(o)) {
int cur = -1, min = -1;
for (int j = 0; j < _selIdxs.size(); j++) {
if (i == _selIdxs.get(j).intValue()) {
cur = j;
} else if (min == -1 || _selIdxs.get(j).intValue() < min) {
min = _selIdxs.get(j).intValue();
}
}
if (cur != -1) {
_jsel = min;
_selIdxs.remove(cur);
smartUpdate("chgSel", getChgSel());
}
break;
}
}
}
}
private String[] getChgSel() {
prepareItems(null, true, _model);
if (_options != null) {
String [] chgSel = _options;
_options = null;
return chgSel;
}
return new String[0];
}
protected boolean isChildable() {
return _childable;
}
private void prepareData() {
if (getAttribute(CHOSENBOX_PREPARE_DATA) != null)
return;
if (_selIdxs.size() > 0)
_chgSel = getChgSel();
if (!(_model instanceof ListSubModel))
prepareItems(null, false, _model);
setAttribute(CHOSENBOX_PREPARE_DATA, Boolean.TRUE);
}
// fix selected indexes while model changed or replaced
private void fixIndexs(boolean modelReplaced, ListDataEvent event) {
// model instance is changed
if (modelReplaced) {
if (_model == null) {
clearSelection();
} else {
// remove the out of range indexes
Iteratorit = _selIdxs.iterator();
while (it.hasNext()) {
if (it.next() >= _model.getSize()) {
it.remove();
}
}
}
} else {
int pos0 = event.getIndex0();
int pos1 = event.getIndex1();
int amount = pos1 - pos0 + 1;
switch (event.getType()) {
case ListDataEvent.INTERVAL_ADDED:
for (int i = 0; i < _selIdxs.size(); i++) {
if (_selIdxs.get(i) >= pos0)
_selIdxs.set(i, _selIdxs.get(i) + amount);
}
break;
case ListDataEvent.INTERVAL_REMOVED:
for(ListIterator lit = _selIdxs.listIterator(); lit.hasNext();) {
Integer i = lit.next();
if (i > pos1)
lit.set(i - amount);
else if (i >= pos0)
lit.remove();
}
break;
}
}
}
/**
* prepare the list content or selected items to render,
* @param prefix
* Only add the item starts with it if it is not null.
* @param excludeUnselected
* Only add selected item, with select order.
* @param model
* the model to render.
*/
private void prepareItems(String prefix, boolean excludeUnselected, ListModel model) {
if (model != null) {
List optList = new ArrayList();
final boolean old = _childable;
try {
_childable = true;
final ItemRenderer renderer = getRealRenderer();
// order by _selIdxs content if only prepare selected items
if (excludeUnselected) {
for (int i = 0; i < _selIdxs.size(); i++) {
String s = renderer.render(this, model.getElementAt(_selIdxs.get(i)), _selIdxs.get(i));
if (prefix == null || s.toLowerCase().startsWith(prefix.toLowerCase()))
optList.add(s);
}
} else {
for (int i = 0; i < model.getSize(); i++) {
String s = renderer.render(this, model.getElementAt(i), i);
if (prefix == null || s.toLowerCase().startsWith(prefix.toLowerCase()))
optList.add(s);
}
}
if (optList.size() > 0)
_options = optList.toArray(new String[0]);
} catch (Exception e) {
throw UiException.Aide.wrap(e);
} finally {
//clear possible children created in renderer
_childable = old;
getChildren().clear();
}
}
}
private void updateItems() {
prepareItems(null, false, _model);
if (_options != null) {
smartUpdate("items", _options);
_options = null; //purge the data
}else {
smartUpdate("chgSel", getChgSel());
}
}
private void updateListContent(String prefix, ListModel subModel) {
if (!(_model instanceof ListSubModel))
prepareItems(null, false, subModel);
else
prepareItems(prefix, false, subModel);
if (_options != null) {
smartUpdate("listContent", _options);
_options = null; //purge the data
} else
smartUpdate("listContent", new String[0]);
}
private void initDataListener() {
if (_dataListener == null)
_dataListener = new ListDataListener() {
public void onChange(ListDataEvent event) {
fixIndexs(false, event);
updateItems();
}
};
if (_eventListener == null)
_eventListener = new EventListener() {
@SuppressWarnings("unchecked")
public void onEvent(InputEvent event) throws Exception {
if (getModel() instanceof ListSubModel) {
updateListContent(null, ((ListSubModel)_model).getSubModel(event.getValue(), _model.getSize()));
}
}
};
_model.addListDataListener(_dataListener);
if (_model instanceof ListSubModel)
addEventListener("onSearching", _eventListener);
}
private Integer getIndexFromValue(String valueHTML, boolean checkSubList) {
String value = StringEscapeUtils.unescapeHtml4(valueHTML);
for (int i = 0; i < _model.getSize(); i++) {
if (value.equals(_model.getElementAt(i).toString()))
return Integer.valueOf(i);
}
if (checkSubList && _subListModel != null) {
for (int i = 0; i < _subListModel.getSize(); i++) {
if (value.equals(_subListModel.getElementAt(i).toString())) {
_model.add(_subListModel.getElementAt(i));
return Integer.valueOf(_model.getSize()-1);
}
}
}
throw new UiException("No such item: " + value);
}
// -- ComponentCtrl --//
public void invalidate() {
prepareData();
super.invalidate();
}
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
throws IOException {
super.renderProperties(renderer);
prepareData();
removeAttribute(CHOSENBOX_PREPARE_DATA);
if (_options != null) {
render(renderer, "items", _options);
_options = null; //purge the data
} else {
render(renderer, "items", new String[0]);
}
if (_chgSel != null) {
render(renderer, "chgSel", _chgSel);
_chgSel = null; //purge the data
} else {
render(renderer, "chgSel", new String[0]);
}
render(renderer, "name", _name);
render(renderer, "disabled", isDisabled());
if (_tabindex != 0)
renderer.render("tabindex", _tabindex);
render(renderer, "emptyMessage", getEmptyMessage());
render(renderer, "noResultsText", getNoResultsText());
render(renderer, "separator", getSeparator());
render(renderer, "createMessage", getCreateMessage());
renderer.render("selectedIndex", _jsel);
renderer.render("creatable", _creatable);
renderer.render("renderByServer", _model instanceof ListSubModel);
render(renderer, "open", _open);
//maintain selected items for listsubmodel/renderbyserver
if (_model instanceof ListSubModel && _selIdxs.size() > 0) {
prepareItems(null, true, _model);
if (_options != null && _options.length > 0) {
response("listContent", new AuSetAttribute(this, "listContent", _options));
response("chgSel", new AuSetAttribute(this, "chgSel", _options));
_options = null; //purge the data
}
}
}
public void service(org.zkoss.zk.au.AuRequest request, boolean everError) {
final String cmd = request.getCommand();
if (cmd.equals(Events.ON_SELECT)) {
List> selItems = (List>)request.getData().get("");
// clear at first
_selIdxs.clear();
_jsel = -1;
for (int i = 0; i < selItems.size(); i++) {
int idx = getIndexFromValue(selItems.get(i).toString(), true);
_selIdxs.add(idx);
if (idx < _jsel || _jsel == -1)
_jsel = idx;
}
final Integer index = getSelectedIndex();
final Set objects = getSelectedObjects();
Events.postEvent(new SelectEvent(Events.ON_SELECT, this, null, null, null,
objects, null, null, null, index, 0));
if (selItems.size() < (getSubListModel() != null ? getSubListModel().getSize() : getModel().getSize())) {
StringBuilder script = new StringBuilder();
script.append("(function(){let w=zk.Widget.$('#").append(getUuid()).append("');");
script.append("w.$n('inp').focus();})()");
Executions.schedule(getDesktop(), e -> {setOpen(true);Clients.evalJavaScript(script.toString());}, new Event("onPostSelect"));
}
_onSelectTimestamp = System.currentTimeMillis();
} else if (cmd.equals(Events.ON_OPEN)) {
_open = (Boolean)request.getData().get("open");
Events.postEvent(OpenEvent.getOpenEvent(request));
} else if (cmd.equals("onSearch")) {
Events.postEvent(new Event("onSearch", this, ((List>)request.getData().get("")).get(0).toString()));
} else if (cmd.equals("onSearching")) {
Object data = ((List>)request.getData().get("")).get(0);
Events.postEvent(new InputEvent(cmd, this, (String)data, _value));
_value = (String)data;
} else if (cmd.equals(Events.ON_OK)) {
_onOkTimestamp = System.currentTimeMillis();
Clients.evalJavaScript("setTimeout(function(){zAu.send(new zk.Event(zk.Widget.$('#"
+ getUuid() + "'), 'onOkTimer', null));}, 100);");
} else if (cmd.equals("onOkTimer")) {
Events.postEvent("onOkTimer", this, null);
}
}
public ListModel getSubListModel() {
return _subListModel;
}
public void setSubListModel(ListModel _subListModel) {
this._subListModel = _subListModel;
}
@Override
public void focus() {
response(new AuScript("$('#"+getUuid()+"-inp').focus();"));
}
private final ItemRenderer _defRend = new ItemRenderer() {
public String render(final Component owner, final T data, final int index) {
final Chosenbox> self = (Chosenbox>) owner;
final Template tm = self.getTemplate("model");
if (tm == null)
return Objects.toString(data);
else {
final Component[] items = tm.create(owner, null,
new VariableResolver() {
public Object resolveVariable(String name) {
if ("each".equals(name)) {
return data;
} else if ("forEachStatus".equals(name)) {
return new ForEachStatus() {
public ForEachStatus getPrevious() {
return null;
}
public Object getEach() {
return data;
}
public int getIndex() {
return index;
}
public Integer getBegin() {
return 0;
}
public Integer getEnd() {
return ((Chosenbox>)owner).getModel().getSize();
}
public boolean isFirst() {
return index == 0;
}
public boolean isLast() {
return (index+1) == ((Chosenbox>)owner).getModel().getSize();
}
public int getCount() {
return index+1;
}
public Object getCurrent() {
return data;
}
public Integer getStep() {
return Integer.valueOf(1);
}
};
} else {
return null;
}
}
}, null);
if (items.length != 1)
throw new UiException(
"The model template must have exactly one item, not "
+ items.length);
if (!(items[0] instanceof Label))
throw new UiException(
"The model template can only support Label component, not "
+ items[0]);
items[0].detach(); //remove the label from owner
return ((Label) items[0]).getValue();
}
}
};
}