Author: kmorin Date: 2013-03-09 19:11:38 +0100 (Sat, 09 Mar 2013) New Revision: 2605 Url: http://nuiton.org/projects/jaxx/repository/revisions/2605 Log: refs #2580 Make a combobox whose elements can be filtered Added: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.css trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.jaxx trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBoxHandler.java Copied: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.css (from rev 2591, trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.css) =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.css (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.css 2013-03-09 18:11:38 UTC (rev 2605) @@ -0,0 +1,95 @@ +/* + * #%L + * JAXX :: Widgets + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2012 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% + */ +#indexes { + useToolTipText:true; +} + +#sortGroup { + useToolTipText:true; + selectedValue:{isReverseSort()}; +} + +#popup { + border:{new TitledBorder(_("beancombobox.popup.title"))}; +} + +#popupSortLabel { + actionIcon:"bean-sort"; + text:"bean.sort.label"; +} + +#sortUp { + buttonGroup:"sortGroup"; + value:{false}; + actionIcon:"bean-sort-up"; + text:"bean.sort.up"; + selected:{!isReverseSort()}; +} + +#sortDown { + buttonGroup:"sortGroup"; + value:{true}; + actionIcon:"bean-sort-down"; + text:"bean.sort.down"; + selected:{isReverseSort()}; +} + +#toolbarLeft { + floatable:false; + borderPainted:false; + visible:{isShowReset()}; +} + +#resetButton { + actionIcon:"combobox-reset"; + toolTipText:"beancombobox.action.reset.tip"; + focusable:false; + focusPainted:false; + enabled:{isEditable() && isEnabled()}; +} + +#combobox { + model: {new JaxxFilterableComboBoxModel<O>()}; + selectedItem:{getSelectedItem()}; + enabled:{isEnabled()}; + editable:{isEditable()}; + focusable:{isEnabled() && isEditable()}; +} + +#toolbarRight { + floatable:false; + borderPainted:false; + visible:{isShowDecorator()}; +} + +#changeDecorator { + actionIcon:"combobox-sort"; + toolTipText:"beancombobox.action.sort.tip"; + focusable:false; + focusPainted:false; + enabled:{isShowDecorator() && isEnabled()}; +} + + + Copied: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.jaxx (from rev 2591, trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBox.jaxx) =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.jaxx (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBox.jaxx 2013-03-09 18:11:38 UTC (rev 2605) @@ -0,0 +1,146 @@ +<!-- + #%L + JAXX :: Widgets + + $Id$ + $HeadURL$ + %% + Copyright (C) 2008 - 2010 CodeLutin + %% + 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% + --> + +<Table fill='both' insets='0' genericType='O' + implements='jaxx.runtime.bean.BeanTypeAware<O>' + onFocusGained='combobox.requestFocus()' onFocusLost='hidePopup()'> + + <import> + jaxx.runtime.swing.model.JaxxFilterableComboBoxModel + org.nuiton.util.decorator.JXPathDecorator + javax.swing.border.TitledBorder + </import> + + <!-- bean type --> + <Class id='beanType' genericType='O' javaBean='null'/> + + <!-- filterable property --> + <Boolean id='filterable' javaBean='false'/> + + <!-- flag to reverse the sort --> + <Boolean id='reverseSort' javaBean='false'/> + + <!-- show reset property --> + <Boolean id='showReset' javaBean='false'/> + + <!-- show decorator property --> + <Boolean id='showDecorator' javaBean='true'/> + + <!-- editable combo property --> + <Boolean id='editable' javaBean='true'/> + + <!-- bean property linked state --> + <String id='property' javaBean='""'/> + + <!-- model property --> + <Object id='bean' javaBean='null'/> + + <!-- selectedItem property --> + <Object id='selectedItem' javaBean='null'/> + + <!-- sort index property --> + <Integer id='index' javaBean='0'/> + + <java.awt.Color id='invalidComboEditorTextColor' javaBean='null'/> + + <!-- datas of the combo-box --> + <java.util.List id='data' genericType='O' javaBean='null'/> + + <!-- model of sorted property --> + <ButtonGroup id='indexes' + onStateChanged='setIndex((Integer)indexes.getSelectedValue())'/> + + <ButtonGroup id='sortGroup' + onStateChanged='setReverseSort((Boolean)sortGroup.getSelectedValue())'/> + + <!-- ui handler --> + <BeanFilterableComboBoxHandler id='handler' genericType='O' constructorParams='this'/> + + <String id='selectedToolTipText' javaBean='null'/> + + <String id='notSelectedToolTipText' javaBean='null'/> + + <String id='popupTitleText' javaBean='null'/> + + <String id='i18nPrefix' javaBean='"entitycombobox.common."'/> + + <!-- popup to change sorted property--> + <JPopupMenu id='popup' + onPopupMenuWillBecomeInvisible='getChangeDecorator().setSelected(false)' + onPopupMenuCanceled='getChangeDecorator().setSelected(false)'> + <JLabel id='popupSortLabel'/> + + <JRadioButtonMenuItem id='sortUp'/> + + <JRadioButtonMenuItem id='sortDown'/> + + <JSeparator id='popupSeparator'/> + <JLabel id='popupLabel'/> + <JSeparator/> + </JPopupMenu> + + <script><![CDATA[ + +public static final String PROPERTY_EMPTY = "empty"; + +public void init(JXPathDecorator<O> decorator, List<O> data) { + handler.init(decorator, data); +} + +protected void hidePopup() { + if (popup.isVisible()) { + popup.setVisible(false); + } +} + +public boolean isEmpty() { return handler.isEmpty(); } + +public void addItem(O item) { handler.addItem(item); } + +public void removeItem(O item) { handler.removeItem(item); } + +]]> + </script> + <row> + <cell anchor='west'> + <!-- le boutton pour reinitialiser la valeur sélectionnée --> + <JToolBar id='toolbarLeft'> + <JButton id='resetButton' onActionPerformed='handler.reset()'/> + </JToolBar> + + </cell> + <cell weightx='1'> + <!-- la liste déroulante --> + <JComboBox id='combobox'/> + </cell> + <cell anchor='east' fill='both' insets='0'> + <!-- le boutton pour changer le tri --> + <JToolBar id='toolbarRight'> + <JToggleButton id='changeDecorator' + onActionPerformed='getHandler().togglePopup()'/> + </JToolBar> + </cell> + </row> +</Table> Copied: trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBoxHandler.java (from rev 2591, trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanComboBoxHandler.java) =================================================================== --- trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBoxHandler.java (rev 0) +++ trunk/jaxx-widgets/src/main/java/jaxx/runtime/swing/editor/bean/BeanFilterableComboBoxHandler.java 2013-03-09 18:11:38 UTC (rev 2605) @@ -0,0 +1,604 @@ +/* + * #%L + * JAXX :: Widgets + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2008 - 2010 CodeLutin + * %% + * 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% + */ +package jaxx.runtime.swing.editor.bean; + +import java.awt.Color; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import jaxx.runtime.SwingUtil; +import jaxx.runtime.swing.JAXXButtonGroup; +import jaxx.runtime.swing.renderer.DecoratorListCellRenderer; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jdesktop.swingx.autocomplete.ObjectToStringConverter; +import org.nuiton.util.decorator.DecoratorUtil; +import org.nuiton.util.decorator.JXPathDecorator; +import org.nuiton.util.decorator.MultiJXPathDecorator; + +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JPopupMenu; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.reflect.Method; +import java.util.List; +import javax.swing.ComboBoxEditor; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.plaf.basic.BasicComboBoxEditor; +import jaxx.runtime.swing.model.JaxxFilterableComboBoxModel; +import org.jdesktop.swingx.autocomplete.AbstractAutoCompleteAdaptor; +import org.jdesktop.swingx.autocomplete.AutoCompleteDocument; +import org.jdesktop.swingx.autocomplete.ComboBoxAdaptor; + +/** + * Le handler d'un {@link BeanFilterableComboBox}. + * <p/> + * Note: ce handler n'est pas stateless et n'est donc pas partageable entre + * plusieurs ui. + * + * @param <O> le type des objet contenus dans le modèle du composant. + * @author kmorin <kmorin@codelutin.com> + * @since 2.5.12 + * @see BeanFilterableComboBox + */ +public class BeanFilterableComboBoxHandler<O> implements PropertyChangeListener { + + public static final Log log = LogFactory.getLog(BeanFilterableComboBoxHandler.class); + /** + * ui if the handler + */ + protected BeanFilterableComboBox<O> ui; + /** + * the mutator method on the property of boxed bean in the ui + */ + protected Method mutator; + /** + * the decorator of data + */ + protected MultiJXPathDecorator<O> decorator; + + protected boolean init; + + public BeanFilterableComboBoxHandler(BeanFilterableComboBox<O> ui) { + this.ui = ui; + } + + protected final FocusListener EDITOR_TEXT_COMP0NENT_FOCUSLISTENER = new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + if (log.isDebugEnabled()) { + log.debug("close popup from " + e); + } + ui.getPopup().setVisible(false); + } + + @Override + public void focusLost(FocusEvent e) { + } + }; + + protected final DocumentListener EDITOR_TEXT_COMPONENT_DOCUMENTLISTENER = new DocumentListener() { + public void insertUpdate(DocumentEvent e) { + updateFilter(); + } + + public void removeUpdate(DocumentEvent e) { + updateFilter(); + } + + public void changedUpdate(DocumentEvent e) { + updateFilter(); + } + + protected void updateFilter() { + JComboBox comboBox = ui.getCombobox(); + JaxxFilterableComboBoxModel model = (JaxxFilterableComboBoxModel) comboBox.getModel(); + JTextComponent editorComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); + // hide the popup before setting the filter, otherwise the popup height does not fit + if (comboBox.isShowing()) { + comboBox.hidePopup(); + } + model.setFilterText(editorComponent.getText()); + if (comboBox.isShowing()) { + comboBox.showPopup(); + } + } + + }; + + private final BeanUIUtil.PopupHandler popupHandler = new BeanUIUtil.PopupHandler() { + @Override + public JPopupMenu getPopup() { + return ui.getPopup(); + } + + @Override + public JComponent getInvoker() { + return ui.getChangeDecorator(); + } + }; + + /** + * Initialise le handler de l'ui + * + * @param decorator le decorateur a utiliser + * @param data la liste des données a gérer + */ + public void init(JXPathDecorator<O> decorator, List<O> data) { + + if (init) { + throw new IllegalStateException("can not init the handler twice"); + } + init = true; + + if (decorator == null) { + throw new NullPointerException("decorator can not be null"); + } + + JAXXButtonGroup indexes = ui.getIndexes(); + + this.decorator = BeanUIUtil.createDecorator(decorator); + + final JComboBox combobox = ui.getCombobox(); + ComboBoxEditor editor = new JAXXFilterableComboBoxEditor(ui.getCombobox().getEditor()); + combobox.setEditor(editor); + + editor.getEditorComponent().addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + combobox.showPopup(); + } + + }); + editor.getEditorComponent().addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (!combobox.isPopupVisible()) { + combobox.showPopup(); + } + } + + }); + + // init combobox renderer base on given decorator + combobox.setRenderer(new DecoratorListCellRenderer(this.decorator)); + ((JaxxFilterableComboBoxModel) combobox.getModel()).setDecorator(this.decorator); + combobox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); + combobox.addItemListener(new ItemListener() { + + public void itemStateChanged(ItemEvent e) { + Object item = e.getItem(); + if (e.getStateChange() == ItemEvent.SELECTED + && item != null && getBeanType().isInstance(item)) { + ui.setSelectedItem(e.getItem()); + } + } + }); + + // build popup + popupHandler.preparePopup(ui.getSelectedToolTipText(), + ui.getNotSelectedToolTipText(), + ui.getI18nPrefix(), + ui.getPopupTitleText(), + indexes, + ui.getPopupSeparator(), + ui.getPopupLabel(), + ui.getSortUp(), + ui.getSortDown(), + this.decorator); + + setFilterable(false, ui.getFilterable()); + + ui.addPropertyChangeListener(this); + + // set datas + ui.setData(data); + + // select sort button + indexes.setSelectedButton(ui.getIndex()); + } + + /** + * Toggle the popup visible state. + */ + public void togglePopup() { + popupHandler.togglePopup(); + } + + /** + * @return {@code true} if there is no data in comboBox, {@code false} + * otherwise. + */ + public boolean isEmpty() { + boolean result = CollectionUtils.isEmpty(ui.getData()); + return result; + } + + /** + * Add the given item into the comboBox. + * <p/> + * <strong>Note:</strong> The item will be inserted at his correct following + * the selected ordering. + * + * @param item item to add in comboBox. + */ + public void addItem(O item) { + + List<O> data = ui.getData(); + boolean wasEmpty = CollectionUtils.isEmpty(data); + data.add(item); + + updateUI(ui.getIndex(), ui.isReverseSort()); + + fireEmpty(wasEmpty); + } + + /** + * Remove the given item from the comboBox model. + * <p/> + * <strong>Note:</strong> If this item was selected, then selection will be + * cleared. + * + * @param item the item to remove from the comboBox model + */ + public void removeItem(O item) { + + List<O> data = ui.getData(); + + boolean remove = data.remove(item); + + if (remove) { + + // item was found in data + + Object selectedItem = ui.getSelectedItem(); + if (item == selectedItem) { + + // item was selected item, reset selected item then + ui.setSelectedItem(null); + } + + updateUI(ui.getIndex(), ui.isReverseSort()); + fireEmpty(false); + } + } + + /** + * Sort data of the model. + */ + public void sortData() { + + // just update UI should do the math of this + updateUI(ui.getIndex(), ui.isReverseSort()); + } + + public void reset() { + JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); + editorComponent.setText(""); +// ui.setSelectedItem(null); + } + + /** + * Modifie l'état filterable de l'ui. + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + protected void setFilterable(Boolean oldValue, Boolean newValue) { + oldValue = oldValue != null && oldValue; + newValue = newValue != null && newValue; + if (oldValue.equals(newValue)) { + return; + } + if (log.isDebugEnabled()) { + log.debug("filterable state : <" + oldValue + " to " + newValue + ">"); + } + if (!newValue) { + JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); + editorComponent.removeFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLISTENER); + editorComponent.getDocument().removeDocumentListener(EDITOR_TEXT_COMPONENT_DOCUMENTLISTENER); + ((JaxxFilterableComboBoxModel) ui.getCombobox().getModel()).setFilterText(null); + + } else { + JTextComponent editorComponent = (JTextComponent) ui.getCombobox().getEditor().getEditorComponent(); + editorComponent.addFocusListener(EDITOR_TEXT_COMP0NENT_FOCUSLISTENER); + editorComponent.getDocument().addDocumentListener(EDITOR_TEXT_COMPONENT_DOCUMENTLISTENER); + EDITOR_TEXT_COMPONENT_DOCUMENTLISTENER.changedUpdate(null); + } + } + + /** + * Modifie l'index du décorateur + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + protected void setIndex(Integer oldValue, Integer newValue) { + if (newValue == null || newValue.equals(oldValue)) { + return; + } + if (log.isDebugEnabled()) { + log.debug("check state : <" + oldValue + " to " + newValue + ">"); + } + updateUI(newValue, ui.isReverseSort()); + } + + /** + * Modifie l'index du décorateur + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + protected void setSortOrder(Boolean oldValue, Boolean newValue) { + + if (newValue == null || newValue.equals(oldValue)) { + return; + } + if (log.isDebugEnabled()) { + log.debug("check state : <" + oldValue + " to " + newValue + ">"); + } + + updateUI(ui.getIndex(), newValue); + } + + protected void updateUI(int index, boolean reversesort) { + + // change decorator context + decorator.setContextIndex(index); + + // keep selected item + Object previousSelectedItem = ui.getSelectedItem(); + + // remove autocomplete + if (previousSelectedItem != null) { + ui.getCombobox().setSelectedItem(null); + ui.selectedItem = null; + } + + List<O> data = ui.getData(); + try { + // Sort data with the decorator jxpath tokens. + DecoratorUtil.sort(decorator, + data, + index, + reversesort); + + } catch (Exception eee) { + log.warn(eee.getMessage(), eee); + } + + // reload the model + SwingUtil.fillComboBox(ui.getCombobox(), data, null); + + if (previousSelectedItem != null) { + ui.setSelectedItem(previousSelectedItem); + } + + ui.getCombobox().requestFocus(); + } + + /** + * Modifie la valeur sélectionnée dans la liste déroulante. + * + * @param oldValue l'ancienne valeur + * @param newValue la nouvelle valeur + */ + protected void setSelectedItem(O oldValue, O newValue) { + if (ui.getBean() == null) { + return; + } + + if (newValue == null) { + if (ui.getCombobox().getSelectedItem() == null) { + return; + } + ui.getCombobox().setSelectedItem(null); + + if (oldValue == null) { + return; + } + } + if (log.isDebugEnabled()) { + log.debug(ui.getProperty() + " on " + getBeanType() + " :: " + oldValue + " to " + newValue); + } + + try { + Method mut = getMutator(); + if (mut != null) { + mut.invoke(ui.getBean(), newValue); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public MultiJXPathDecorator<O> getDecorator() { + return decorator; + } + + /** + * @return get the type of objects contained in the comboBox model. + */ + public Class<O> getBeanType() { + Class<O> result = ui.getBeanType(); + if (result == null) { + result = decorator == null ? null : decorator.getType(); + } + return result; + } + + /** + * @return le mutateur a utiliser pour modifier le bean associé. + */ + protected Method getMutator() { + if (mutator == null && ui.getBean() != null && ui.getProperty() != null) { + mutator = BeanUIUtil.getMutator(ui.getBean(), ui.getProperty()); + } + return mutator; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + String propertyName = evt.getPropertyName(); + + if (BeanFilterableComboBox.PROPERTY_SELECTED_ITEM.equals(propertyName)) { + setSelectedItem((O) evt.getOldValue(), (O) evt.getNewValue()); + return; + } + + if (BeanFilterableComboBox.PROPERTY_FILTERABLE.equals(propertyName)) { + + setFilterable((Boolean) evt.getOldValue(), + (Boolean) evt.getNewValue()); + return; + } + + if (BeanFilterableComboBox.PROPERTY_INDEX.equals(propertyName)) { + + // decorator index has changed, force reload of data in ui + setIndex((Integer) evt.getOldValue(), + (Integer) evt.getNewValue()); + return; + } + + if (BeanFilterableComboBox.PROPERTY_REVERSE_SORT.equals(propertyName)) { + + // sort order has changed, force reload of data in ui + setSortOrder((Boolean) evt.getOldValue(), + (Boolean) evt.getNewValue()); + return; + } + + if (BeanFilterableComboBox.PROPERTY_DATA.equals(propertyName)) { + + // list has changed, force reload of index + setIndex(null, ui.getIndex()); + + // list has changed, fire empty property + List list = (List) evt.getOldValue(); + fireEmpty(CollectionUtils.isEmpty(list)); + } + } + + protected void fireEmpty(boolean wasEmpty) { + ui.firePropertyChange(BeanComboBox.PROPERTY_EMPTY, wasEmpty, + isEmpty()); + } + + /** + * Editor for the Combobox of the UI - uses the decorator + */ + class JAXXFilterableComboBoxEditor implements ComboBoxEditor { + + Object oldItem; + ComboBoxEditor wrapped; + + public JAXXFilterableComboBoxEditor(ComboBoxEditor wrapped) { + this.wrapped = wrapped; + } + + @Override + public JTextComponent getEditorComponent() { + return (JTextComponent) wrapped.getEditorComponent(); + } + + @Override + public void setItem(Object anObject) { + Object item = anObject; + if (anObject != null && getBeanType().isInstance(anObject)) { + item = decorator.toString(anObject); + } + oldItem = anObject; + try { + wrapped.setItem(item); + getEditorComponent().setForeground(null); + + } catch (IllegalStateException e) { + // fail silently and deselect the currently selected item + ui.setSelectedItem(null); + getEditorComponent().setForeground(ui.getInvalidComboEditorTextColor()); + } + } + + @Override + public Object getItem() { + JTextComponent editor = getEditorComponent(); + Object newValue = editor.getText(); + + if (oldItem != null && getBeanType().isInstance(oldItem)) { + // The original value is not a string. Should return the value in it's + // original type. + if (newValue.equals(decorator.toString(oldItem))) { + newValue = oldItem; + + } else { + // Must take the value from the editor and get the value and cast it to the new type. + Class<?> cls = oldItem.getClass(); + try { + Method method = cls.getMethod("valueOf", new Class[]{String.class}); + newValue = method.invoke(oldItem, new Object[] { editor.getText()}); + } catch (Exception ex) { + // Fail silently and return the newValue (a String object) + } + } + } + wrapped.getItem(); + return newValue; + } + + @Override + public void selectAll() { + wrapped.selectAll(); + } + + @Override + public void addActionListener(ActionListener l) { + wrapped.addActionListener(l); + } + + @Override + public void removeActionListener(ActionListener l) { + wrapped.removeActionListener(l); + } + }; +}