package org.rr.commons.swing.components; import static org.rr.commons.utils.StringUtil.EMPTY; import java.awt.Component; import java.util.HashSet; import java.util.List; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.MutableComboBoxModel; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentListener; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.rr.commons.swing.SwingUtils; import org.rr.commons.utils.ListUtils; import org.rr.commons.utils.StringUtil; public class JREditableHistoryComboBox extends JRComboBox<String> { private static final long serialVersionUID = 8405381419161925378L; static class FilterFieldComboboxEditor extends BasicComboBoxEditor { public Component getEditorComponent() { SwingUtils.setColor(editor, false); editor.setSelectedTextColor(SwingUtils.getSelectionForegroundColor()); editor.setSelectionColor(SwingUtils.getSelectionBackgroundColor()); return editor; } /** * Creates the internal editor component. Override this to provide a custom implementation. * * @return a new editor component * @since 1.6 */ protected JTextField createEditorComponent() { JTextField editor = new BorderlessTextField(EMPTY, 9); editor.setBorder(null); return editor; } static class BorderlessTextField extends JTextField { public BorderlessTextField(String value, int n) { super(value, n); } // workaround for 4530952 public void setText(String s) { if (getText().equals(s)) { return; } super.setText(s); } public void setBorder(Border b) { if (!(b instanceof UIResource)) { super.setBorder(b); } } } } private final BasicComboBoxEditor comboboxEditor = new FilterFieldComboboxEditor(); public JREditableHistoryComboBox() { super(); setModel(new DefaultComboBoxModel<String>()); setEditable(true); setEditor(comboboxEditor); ((JComponent) comboboxEditor.getEditorComponent()).setBorder(new EmptyBorder(0, 5, 0, 5)); } /** * Tells the text filter field to display it self in and active filter color. */ public void enableFilterColor(boolean enable) { if (enable) { ((JTextComponent) comboboxEditor.getEditorComponent()).setBackground(SwingUtils.getSelectionBackgroundColor()); ((JTextComponent) comboboxEditor.getEditorComponent()).setForeground(SwingUtils.getSelectionForegroundColor()); ((JTextComponent) comboboxEditor.getEditorComponent()).setSelectionColor(SwingUtils.getSelectionBackgroundColor().brighter()); } else { ((JTextComponent) comboboxEditor.getEditorComponent()).setForeground(SwingUtils.getForegroundColor()); ((JTextComponent) comboboxEditor.getEditorComponent()).setBackground(SwingUtils.getBackgroundColor()); ((JTextComponent) comboboxEditor.getEditorComponent()).setSelectionColor(SwingUtils.getSelectionBackgroundColor()); } } /** * Set the item as editor value. * * @param item The item to be used for as the current editor value. */ public void setItem(String item) { comboboxEditor.setItem(item); } /** * Add the given comma separated values to the history. * * @param entries The entries to be put to the history. */ public void setHistoryValues(String entries) { final MutableComboBoxModel<String> model = (MutableComboBoxModel<String>) getModel(); if (entries != null && entries.length() > 0) { List<String> split = ListUtils.split(entries, ","); for (String string : split) { model.addElement(string); } } updateUI(); } /** * Get all history values as comma separated string. * * @return The comma separated history values. */ public String getHistoryValues() { ComboBoxModel<String> model = getModel(); StringBuilder modelEntries = new StringBuilder(); for (int i = 0; i < model.getSize(); i++) { String elementAt = StringUtil.replace(StringUtil.toString(model.getElementAt(i)), ",", EMPTY); if (modelEntries.length() > 0) { modelEntries.append(","); } modelEntries.append(elementAt); } return modelEntries.toString(); } /** * Adds the given value to the beginning of the combobox list. If the list exceeds more than 10 entries, the last ones will be deleted. * * @param value The search expression to be attached. */ public void addHistoryValue(final String value) { if (value != null && value.length() > 0) { MutableComboBoxModel<String> model = (MutableComboBoxModel<String>) getModel(); model.insertElementAt(value, 0); if (model.getSize() > 10) { model.removeElementAt(model.getSize() - 1); } removeDuplicateElementsFromFilterModel(value); } } /** * If the selected expression or any other one is more than one times in the filter history list, the last ones will be removed. * * @param selectedExpression * The currently selected filter expression. */ private void removeDuplicateElementsFromFilterModel(final String selectedExpression) { MutableComboBoxModel<String> model = (MutableComboBoxModel<String>) getModel(); HashSet<String> entries = new HashSet<>(model.getSize()); for (int i = 0; i < model.getSize(); i++) { String elementAt = (String) model.getElementAt(i); if (entries.contains(elementAt)) { model.removeElementAt(i); i--; } entries.add(elementAt); } model.setSelectedItem(selectedExpression); } /** * Gets the text from the filter/search field. * * @return The text from the filter/search field. Never returns <code>null</code>. */ public String getEditorValue() { return StringUtil.toString(getEditor().getItem()); } /** * Registers the given observer to begin receiving notifications * when changes are made to the document. * * @param listener the observer to register * @see Document#removeDocumentListener */ public void addDocumentListener(DocumentListener listener) { ((JTextField) comboboxEditor.getEditorComponent()).getDocument().addDocumentListener(listener); } }