diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java
new file mode 100644
index 000000000..439246bb8
--- /dev/null
+++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java
@@ -0,0 +1,275 @@
+/*
+ * This file is part of ###PROJECT_NAME###
+ *
+ * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
+ * Desenvolvemento Tecnolóxico de Galicia
+ *
+ * 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 .
+ */
+
+package org.navalplanner.web.common.components.bandboxsearch;
+
+import static org.navalplanner.web.I18nHelper._;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+import org.navalplanner.web.common.components.finders.IMultipleFiltersFinder;
+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.WrongValueException;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.EventListener;
+import org.zkoss.zk.ui.event.InputEvent;
+import org.zkoss.zul.Bandbox;
+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.api.Listbox;
+
+/**
+ * @author Susana Montes Pedreira
+ */
+
+@SuppressWarnings("serial")
+public class BandboxMultipleSearch extends HtmlMacroComponent {
+
+ private Listbox listbox;
+
+ private Listhead listhead;
+
+ private Bandbox bandbox;
+
+ private String widthBandbox;
+
+ private String widthListbox;
+
+ private IMultipleFiltersFinder multipleFiltersFinder;
+
+ private List selectedFilters = new ArrayList();
+
+ private String selectedFiltersText = new String("");
+
+ public void afterCompose() {
+ super.afterCompose();
+ listbox = (Listbox) getFellowIfAny("listbox");
+ listhead = (Listhead) listbox.getFellowIfAny("listhead");
+ bandbox = (Bandbox) getFellowIfAny("bandbox");
+
+ if (multipleFiltersFinder != null) {
+ multipleFiltersFinder.init();
+ listbox.setModel(new SimpleListModel(multipleFiltersFinder
+ .getMatching("")));
+ listbox.setItemRenderer(multipleFiltersFinder.getItemRenderer());
+ addHeaders();
+
+ /**
+ * Search for matching elements while typing on bandbox
+ */
+ bandbox.addEventListener("onChanging", new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ final String inputText = ((InputEvent) event).getValue();
+ if ((inputText == null) || (inputText.isEmpty())) {
+ clear();
+ } else {
+ String newFilterText = getNewFilterText(inputText);
+ if ((newFilterText != null)
+ && (newFilterText.length() > 2)) {
+ listbox.setModel(getSubModel(newFilterText));
+ listbox.invalidate();
+ } else {
+ clearListbox();
+ }
+ }
+ }
+ });
+
+ /**
+ * Pick element from list when selecting
+ */
+ listbox.addEventListener("onSelect", new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ final Object object = getSelectedItem().getValue();
+ addSelectedElement(object);
+ bandbox.close();
+ clearListbox();
+ }
+ });
+ }
+
+ updateWidth();
+ }
+
+ private String getNewFilterText(String inputText){
+ String newFilterText = new String("");
+ String[] filtersText = inputText.split(",");
+ newFilterText = getLastText(filtersText);
+ newFilterText = newFilterText.replace(" ", "");
+ newFilterText = newFilterText.trim();
+ return newFilterText;
+ }
+
+ private String getLastText(String[] texts) {
+ Integer last = texts.length - 1;
+ if (texts.length > 0) {
+ return texts[last];
+ } else {
+ return "";
+ }
+ }
+
+ private void clearSelectedElement() {
+ bandbox.setValue("");
+ selectedFiltersText = "";
+ selectedFilters.clear();
+ }
+
+ public void addSelectedElement(Object obj) {
+ if (obj != null) {
+ selectedFiltersText = selectedFiltersText
+ .concat(multipleFiltersFinder.objectToString(obj));
+ bandbox.setValue(selectedFiltersText);
+ selectedFilters.add(obj);
+ }
+ }
+
+ public List getSelectedElements() {
+ if (this.multipleFiltersFinder != null) {
+ if (!multipleFiltersFinder.isValidFormatText(selectedFilters,
+ bandbox.getValue())) {
+ throw new WrongValueException(bandbox,
+ _("format filters are not valid"));
+ }
+ }
+ return selectedFilters;
+ }
+
+ /**
+ * Find {@link Label} which name or type start with prefix
+ * @param inputText
+ */
+ @SuppressWarnings("unchecked")
+ private ListModel getSubModel(String inputText) {
+ List result = multipleFiltersFinder.getMatching(inputText);
+ return new SimpleListModel(result);
+ }
+
+ /**
+ * Append headers to listbox header list
+ */
+ @SuppressWarnings("unchecked")
+ public void addHeaders() {
+ clearHeaderIfNecessary();
+ final String[] headers = multipleFiltersFinder.getHeaders();
+ for (int i = 0; i < headers.length; i++) {
+ listhead.getChildren().add(new Listheader(headers[i]));
+ }
+ }
+
+ private void clearHeaderIfNecessary() {
+ if (listhead.getChildren() != null) {
+ listhead.getChildren().clear();
+ }
+ }
+
+ private Listitem getSelectedItem() {
+ return (Listitem) listbox.getSelectedItems().iterator().next();
+ }
+
+ public void setDisabled(boolean disabled) {
+ bandbox.setDisabled(disabled);
+ }
+
+ private Object getBean(String beanName) {
+ HttpServletRequest servletRequest = (HttpServletRequest) Executions
+ .getCurrent().getNativeRequest();
+ ServletContext servletContext = servletRequest.getSession()
+ .getServletContext();
+ WebApplicationContext webApplicationContext = WebApplicationContextUtils
+ .getWebApplicationContext(servletContext);
+ return webApplicationContext.getBean(beanName);
+ }
+
+ public String getFinder() {
+ return multipleFiltersFinder.getClass().toString();
+ }
+
+ public void setFinder(String classname) {
+ multipleFiltersFinder = (IMultipleFiltersFinder) getBean(StringUtils
+ .uncapitalize(classname));
+ }
+
+ /**
+ * Clears {@link Bandbox} Fills bandbox list model, clear bandbox textbox,
+ * and set selected label to null
+ * @param bandbox
+ */
+ public void clear() {
+ clearListbox();
+ clearSelectedElement();
+ }
+
+ private void clearListbox() {
+ List