/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2012 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.common.components.bandboxsearch;
import static org.libreplan.web.I18nHelper._;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.web.common.Util;
import org.libreplan.web.common.components.finders.IBandboxFinder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zkplus.databind.DataBinder;
import org.zkoss.zul.Bandbox;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.SimpleListModel;
import org.zkoss.zul.Listbox;
@SuppressWarnings("serial")
public class BandboxSearch extends HtmlMacroComponent {
private final String SELECTED_ELEMENT_ATTRIBUTE = "selectedElement";
private Listbox listbox;
private Listhead listhead;
private Bandbox bandbox;
private IBandboxFinder finder;
private String widthBandbox;
private String widthListbox;
private List<? extends BaseEntity> model;
@Override
public void afterCompose() {
super.afterCompose();
listbox = (Listbox) getFellowIfAny("listbox");
if ( model != null ) {
setModel(new SimpleListModel<>(model));
} else {
listbox.setModel(finder.getModel());
}
listbox.setItemRenderer(finder.getItemRenderer());
listhead = (Listhead) listbox.getFellowIfAny("listhead");
bandbox = (Bandbox) getFellowIfAny("bandbox");
/* Search for matching elements while typing on bandbox */
bandbox.addEventListener("onChanging", event -> {
clearSelectedElement();
final String inputText = ((InputEvent) event).getValue();
listbox.setModel(getSubModel(inputText));
listbox.invalidate();
});
bandbox.setCtrlKeys("#down");
bandbox.addEventListener(Events.ON_CTRL_KEY, event -> {
int selectedItemIndex = listbox.getSelectedIndex();
if ( selectedItemIndex != -1 ) {
listbox.getItemAtIndex(selectedItemIndex).setFocus(true);
} else {
List<Listitem> items = listbox.getItems();
if ( !items.isEmpty() ) {
listbox.setSelectedIndex(0);
pickElementFromList();
items.get(0).setFocus(true);
}
}
});
/* Pick element from list when selecting */
listbox.addEventListener(Events.ON_SELECT, event -> pickElementFromList());
// Close bandbox for events onClick and onOK
listbox.addEventListener(Events.ON_CLICK, event -> close());
listbox.addEventListener(Events.ON_OK, event -> {
pickElementFromList();
close();
});
addHeaders();
updateWidth();
}
public static BandboxSearch create(String finderClassName, List<? extends BaseEntity> model) {
BandboxSearch bandboxSearch = new BandboxSearch();
bandboxSearch.setFinder(finderClassName);
bandboxSearch.afterCompose();
bandboxSearch.setModel(model);
return bandboxSearch;
}
public static BandboxSearch create(String finderClassName) {
BandboxSearch bandboxSearch = new BandboxSearch();
bandboxSearch.setFinder(finderClassName);
bandboxSearch.afterCompose();
return bandboxSearch;
}
public void pickElementFromList() {
final Object object = getSelectedItem().getValue();
bandbox.setValue(finder.objectToString(object));
setSelectedElement(object);
Util.getBinder(this).saveAttribute(this, SELECTED_ELEMENT_ATTRIBUTE);
}
private void clearSelectedElement() {
setSelectedElement(null);
}
public void setSelectedElement(Object obj) {
bandbox.setAttribute(SELECTED_ELEMENT_ATTRIBUTE, obj, true);
if ( obj != null ) {
bandbox.setValue(finder.objectToString(obj));
} else {
bandbox.setValue("");
}
/* TODO resolve deprecated */
DataBinder binder = Util.getBinder(this);
if (binder != null) {
binder.saveAttribute(this, SELECTED_ELEMENT_ATTRIBUTE);
}
}
public Object getSelectedElement() {
return bandbox.getAttribute(SELECTED_ELEMENT_ATTRIBUTE, true);
}
/**
* Find {@link Label} which name or type start with prefix.
*
* @param inputText
*/
@SuppressWarnings("unchecked")
private ListModel<Object> getSubModel(String inputText) {
List<Object> result = new ArrayList<>();
final ListModel finderModel = finder.getModel();
for (int i = 0; i < finderModel.getSize(); i++) {
Object obj = finderModel.getElementAt(i);
if (finder.entryMatchesText(obj, inputText)) {
result.add(obj);
}
}
return new SimpleListModel<>(result);
}
/**
* Append headers to listbox header list.
*/
@SuppressWarnings("unchecked")
private void addHeaders() {
clearHeaderIfNecessary();
final String[] headers = finder.getHeaders();
for (String header : headers) {
listhead.getChildren().add(new Listheader(_(header)));
}
}
private void clearHeaderIfNecessary() {
if ( listhead.getChildren() != null ) {
listhead.getChildren().clear();
}
}
public Listitem getSelectedItem() {
return (listbox == null || listbox.getSelectedItems().isEmpty())
? null
: listbox.getSelectedItems().iterator().next();
}
public String getFinder() {
return finder.getClass().toString();
}
public void setFinder(String classname) {
finder = (IBandboxFinder) getBean(StringUtils.uncapitalize(classname));
finder.resetModel();
}
public List<? extends BaseEntity> getModel() {
return model;
}
public void setModel(List<? extends BaseEntity> model) {
this.model = model;
setModel(new SimpleListModel<>(model));
}
private void setModel(ListModel<Object> model) {
finder.setModel(model);
listbox.setModel(model);
}
public void setDisabled(boolean disabled) {
bandbox.setDisabled(disabled);
}
private Object getBean(String classname) {
HttpServletRequest servletRequest = (HttpServletRequest) Executions.getCurrent().getNativeRequest();
ServletContext servletContext = servletRequest.getSession().getServletContext();
WebApplicationContext webApplicationContext =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
return webApplicationContext.getBean(classname);
}
/**
* Clears {@link Bandbox}.
*
* Fills bandbox list model, clear bandbox textbox, and set selected label to null.
*/
public void clear() {
listbox.setModel(finder.getModel());
bandbox.setValue("");
clearSelectedElement();
}
/**
* Adds a new element to list of elements.
*
* @param obj
*/
public void addElement(Object obj) {
List<Object> list = asList(finder.getModel());
list.add(obj);
setModel(new SimpleListModel<>(list));
Util.reloadBindings(listbox);
}
public List<Object> asList(ListModel model) {
List<Object> result = new ArrayList<>();
for (int i = 0; i < model.getSize(); i++) {
result.add(model.getElementAt(i));
}
return result;
}
public void setListboxEventListener(String event, EventListener listener) {
listbox.addEventListener(event, listener);
}
public void setBandboxEventListener(String event, EventListener listener) {
bandbox.addEventListener(event, listener);
}
public void foucusOnInput() {
bandbox.setFocus(true);
}
public String getWidthBandbox() {
return widthBandbox;
}
public void setWidthBandbox(String widthBandbox) {
this.widthBandbox = widthBandbox;
}
public String getWidthListbox() {
return widthListbox;
}
public void setWidthListbox(String widthListbox) {
this.widthListbox = widthListbox;
}
private void updateWidth() {
if ( (widthBandbox != null) && (!widthBandbox.isEmpty()) ) {
this.bandbox.setWidth(widthBandbox);
this.listbox.setWidth(widthListbox);
}
}
public void setListboxWidth(String width) {
if ( listbox != null ) {
listbox.setWidth(width);
}
}
public void close() {
if ( bandbox != null ) {
bandbox.close();
}
}
public void setBandboxConstraint(String constraint) {
bandbox.setConstraint(constraint);
}
public void setBandboxConstraint(Constraint constraint) {
bandbox.setConstraint(constraint);
}
}