package com.revolsys.swing.field; import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.Closeable; import java.util.Arrays; import java.util.Collection; import java.util.function.Function; import javax.swing.ComboBoxEditor; import javax.swing.ComboBoxModel; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.text.JTextComponent; import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator; import com.revolsys.datatype.DataType; import com.revolsys.util.Exceptions; import com.revolsys.util.Strings; public class ComboBox<T> extends JComboBox<T> implements Field, KeyListener { private static final long serialVersionUID = 1L; public static <V> ComboBox<V> newComboBox(final String fieldName, final Collection<V> items) { final ComboBoxModel<V> model = newModel(items); return newComboBox(fieldName, model); } public static <V> ComboBox<V> newComboBox(final String fieldName, final Collection<V> items, final Function<Object, String> converter) { final ComboBoxModel<V> model = newModel(items); return newComboBox(fieldName, model, converter); } public static <V> ComboBox<V> newComboBox(final String fieldName, final Collection<V> items, final ListCellRenderer<V> renderer) { final ComboBoxModel<V> model = newModel(items); return new ComboBox<>(fieldName, model, null, renderer); } public static <V> ComboBox<V> newComboBox(final String fieldName, final ComboBoxModel<V> model) { return newComboBox(fieldName, model, (Function<Object, String>)null); } public static <V> ComboBox<V> newComboBox(final String fieldName, final ComboBoxModel<V> model, final Function<Object, String> converter) { return new ComboBox<>(fieldName, model, converter, null); } public static <V> ComboBox<V> newComboBox(final String fieldName, final ComboBoxModel<V> model, final ListCellRenderer<V> renderer) { return new ComboBox<>(fieldName, model, null, renderer); } @SuppressWarnings("unchecked") public static <V> ComboBox<V> newComboBox(final String fieldName, final V... items) { return newComboBox(fieldName, Arrays.asList(items)); } public static <V> ArrayListComboBoxModel<V> newModel(final Collection<V> items) { return new ArrayListComboBoxModel<>(items); } @SuppressWarnings("unchecked") public static <V> ArrayListComboBoxModel<V> newModel(final V... items) { return new ArrayListComboBoxModel<>(items); } private final FieldSupport fieldSupport; public ComboBox(final String fieldName, final ComboBoxModel<T> model, Function<Object, String> converter, final ListCellRenderer<T> renderer) { super(model); setEditable(false); if (renderer != null) { setRenderer(renderer); } if (converter == null) { converter = newDefaultConverter(); } final FunctionStringConverter<T> stringConverter = new FunctionStringConverter<>(converter); if (renderer == null) { setRenderer(stringConverter); } AutoCompleteDecorator.decorate(this, stringConverter); final JComponent editorComponent = (JComponent)getEditor().getEditorComponent(); this.fieldSupport = new FieldSupport(this, editorComponent, fieldName, null, true); addActionListener((final ActionEvent e) -> { final Object selectedItem = getSelectedItem(); setFieldValue(selectedItem); }); } @Override public Field clone() { try { return (Field)super.clone(); } catch (final CloneNotSupportedException e) { return Exceptions.throwUncheckedException(e); } } @Override protected void finalize() throws Throwable { final ComboBoxModel<T> model = getModel(); if (model instanceof Closeable) { ((Closeable)model).close(); } super.finalize(); } @Override public void firePropertyChange(final String propertyName, final Object oldValue, final Object newValue) { super.firePropertyChange(propertyName, oldValue, newValue); } @SuppressWarnings("unchecked") public <V extends ComboBoxModel<T>> V getComboBoxModel() { return (V)super.getModel(); } @Override public Color getFieldSelectedTextColor() { final ComboBoxEditor editor = getEditor(); final Component component = editor.getEditorComponent(); if (component instanceof JTextComponent) { final JTextComponent textField = (JTextComponent)component; return textField.getSelectedTextColor(); } else { return Field.DEFAULT_SELECTED_FOREGROUND; } } @Override public FieldSupport getFieldSupport() { return this.fieldSupport; } @Override public T getSelectedItem() { return (T)super.getSelectedItem(); } @Override public void keyPressed(final KeyEvent e) { } @Override public void keyReleased(final KeyEvent e) { } @Override public void keyTyped(final KeyEvent e) { final char keyChar = e.getKeyChar(); if (keyChar == KeyEvent.VK_BACK_SPACE || keyChar == KeyEvent.VK_DELETE) { final Component editorComponent = getEditor().getEditorComponent(); if (editorComponent instanceof JTextField) { final JTextField textField = (JTextField)editorComponent; final String selectedText = textField.getSelectedText(); final String text = textField.getText(); if (Strings.equals(selectedText, text)) { setSelectedItem(null); } } } } private final <V> Function<V, String> newDefaultConverter() { return (value) -> { if (value == null) { return null; } else { return value.toString(); } }; } @Override public void setEditor(final ComboBoxEditor editor) { final ComboBoxEditor oldEditor = getEditor(); super.setEditor(editor); editor.getEditorComponent().addKeyListener(this); if (oldEditor != null) { oldEditor.getEditorComponent().removeKeyListener(this); } } @Override public void setFieldBackgroundColor(Color color) { if (color == null) { color = Field.DEFAULT_BACKGROUND; } final ComboBoxEditor editor = getEditor(); final Component component = editor.getEditorComponent(); component.setBackground(color); } @Override public void setFieldForegroundColor(Color color) { if (color == null) { color = Field.DEFAULT_FOREGROUND; } final ComboBoxEditor editor = getEditor(); final Component component = editor.getEditorComponent(); component.setForeground(color); } @Override public void setFieldSelectedTextColor(Color color) { if (color == null) { color = Field.DEFAULT_SELECTED_FOREGROUND; } final ComboBoxEditor editor = getEditor(); final Component component = editor.getEditorComponent(); if (component instanceof JTextComponent) { final JTextComponent textField = (JTextComponent)component; textField.setSelectedTextColor(color); } } @Override public void setFieldToolTip(final String toolTip) { final ComboBoxEditor editor = getEditor(); final JComponent component = (JComponent)editor.getEditorComponent(); component.setToolTipText(toolTip); } @Override public synchronized boolean setFieldValue(final Object value) { if (!DataType.equal(getSelectedItem(), value)) { setSelectedItem(value); } return this.fieldSupport.setValue(value); } @Override public void setToolTipText(final String text) { final FieldSupport fieldSupport = getFieldSupport(); if (fieldSupport == null || fieldSupport.setOriginalTooltipText(text)) { super.setToolTipText(text); } } @Override public String toString() { return getFieldName() + "=" + getFieldValue(); } @Override public void updateFieldValue() { setFieldValue(getSelectedItem()); } }