Author: kmorin Date: 2013-03-09 19:11:29 +0100 (Sat, 09 Mar 2013) New Revision: 2604 Url: http://nuiton.org/projects/jaxx/repository/revisions/2604 Log: refs #2580 Make a combobox whose elements can be filtered Added: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/model/JaxxFilterableComboBoxModel.java Copied: trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/model/JaxxFilterableComboBoxModel.java (from rev 2587, trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/model/JaxxDefaultComboBoxModel.java) =================================================================== --- trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/model/JaxxFilterableComboBoxModel.java (rev 0) +++ trunk/jaxx-runtime/src/main/java/jaxx/runtime/swing/model/JaxxFilterableComboBoxModel.java 2013-03-09 18:11:29 UTC (rev 2604) @@ -0,0 +1,202 @@ +package jaxx.runtime.swing.model; + +/* + * #%L + * JAXX :: Runtime + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2013 CodeLutin, Tony Chemit + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import javax.swing.DefaultComboBoxModel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Vector; +import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; +import org.nuiton.util.decorator.JXPathDecorator; + +/** + * ComboBoxModel which can filter the elements displayed in the popup. + * + * @author kmorin <kmorin@codelutin.com> + * @since 2.5.12 + */ +public class JaxxFilterableComboBoxModel<E> extends JaxxDefaultComboBoxModel<E> { + + private static final long serialVersionUID = 1L; + + protected ArrayList<E> filteredItems = Lists.newArrayList(); + + protected String filterText; + + /** the decorator of data */ + protected JXPathDecorator<E> decorator; + + protected List<Predicate<E>> filters = Lists.newArrayList(); + + public JaxxFilterableComboBoxModel() { + super(); + } + + public JaxxFilterableComboBoxModel(E... items) { + delegate = new ArrayList<E>(items.length); + + int i, c; + for (i = 0, c = items.length; i < c; i++) + delegate.add(items[i]); + + } + + public JaxxFilterableComboBoxModel(Collection<E> v) { + delegate = new ArrayList<E>(v); + } + + @Override + public int getIndexOf(E anObject) { + return filteredItems.indexOf(anObject); + } + + @Override + public void addAllElements(Collection<E> objects) { + super.addAllElements(objects); + refilter(); + } + + /** Empties the list. */ + @Override + public void removeAllElements() { + super.removeAllElements(); + refilter(); + } + + @Override + public int getSize() { + return filteredItems.size(); + } + + @Override + public E getElementAt(int index) { + E result; + + if (index >= 0 && index < filteredItems.size()) { + result = filteredItems.get(index); + } else { + result = null; + } + return result; + } + + @Override + public void addElement(Object anObject) { + super.addElement(anObject); + refilter(); + } + + @Override + public void insertElementAt(Object anObject, int index) { + super.insertElementAt(anObject, index); + refilter(); + } + + @Override + public void removeElementAt(int index) { + super.removeElementAt(index); + refilter(); + } + + @Override + public void removeElement(Object anObject) { + super.removeElement(anObject); + refilter(); + } + + public String getFilterText() { + return filterText; + } + + public void setFilterText(String filterText) { + this.filterText = filterText; + refilter(); + } + + public JXPathDecorator<E> getDecorator() { + return decorator; + } + + public void setDecorator(JXPathDecorator<E> decorator) { + this.decorator = decorator; + } + + public void addFilter(Predicate<E> filter) { + filters.add(filter); + refilter(); + } + + public void removeFilter(Predicate<E> filter) { + filters.remove(filter); + refilter(); + } + + public void clearFilters() { + filters.clear(); + refilter(); + } + + protected void refilter() { + filteredItems.clear(); + + if (StringUtils.isEmpty(StringUtils.remove(filterText, '*')) + && filters.isEmpty()) { + filteredItems.addAll(delegate); + + } else { + int itemNumber = delegate.size(); + Pattern pattern = null; + if (!StringUtils.isBlank(filterText)) { + String patternText = Pattern.quote(filterText).replace("*", "\\E.*\\Q") + ".*"; + pattern = Pattern.compile(patternText, Pattern.CASE_INSENSITIVE); + } + for (int i = 0 ; i < itemNumber ; i++) { + E element = delegate.get(i); + boolean addElement = true; + for (Predicate<E> filter : filters) { + addElement &= filter.apply(element); + } + String decoratedElement; + if (decorator != null) { + decoratedElement = decorator.toString(element); + } else { + decoratedElement = String.valueOf(element); + } + boolean matches = pattern == null + || pattern.matcher(decoratedElement).matches(); + if (matches && addElement) { + filteredItems.add(element); + } + } + } + + fireContentsChanged(this, 0, getSize()); + } +}