Autocomplete Combobox In Java With Filtering And Inserting New Text
Join the DZone community and get the full member experience.
Join For Free
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
/**
* Autocomplete combobox with filtering and
* text inserting of new text
* @author Exterminator13
*/
public class AutoCompleteCombo extends JComboBox{
private static final Logger logger = Logger.getLogger(AutoCompleteCombo.class);
private Model model = new Model();
private final JTextComponent textComponent = (JTextComponent) getEditor().getEditorComponent();
private boolean modelFilling = false;
private boolean updatePopup;
public AutoCompleteCombo() {
setEditable(true);
logger.debug("setPattern() called from constructor");
setPattern(null);
updatePopup = false;
textComponent.setDocument(new AutoCompleteDocument());
setModel(model);
setSelectedItem(null);
new Timer(20, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (updatePopup && isDisplayable()) {
setPopupVisible(false);
if (model.getSize() > 0) {
setPopupVisible(true);
}
updatePopup = false;
}
}
}).start();
}
private class AutoCompleteDocument extends PlainDocument {
boolean arrowKeyPressed = false;
public AutoCompleteDocument() {
textComponent.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_ENTER) {
logger.debug("[key listener] enter key pressed");
//there is no such element in the model for now
String text = textComponent.getText();
if (!model.data.contains(text)) {
logger.debug("addToTop() called from keyPressed()");
addToTop(text);
}
} else if (key == KeyEvent.VK_UP ||
key == KeyEvent.VK_DOWN) {
arrowKeyPressed = true;
logger.debug("arrow key pressed");
}
}
});
}
void updateModel() throws BadLocationException {
String textToMatch = getText(0, getLength());
logger.debug("setPattern() called from updateModel()");
setPattern(textToMatch);
}
@Override
public void remove(int offs, int len) throws BadLocationException {
if (modelFilling) {
logger.debug("[remove] model is being filled now");
return;
}
super.remove(offs, len);
if (arrowKeyPressed) {
arrowKeyPressed = false;
logger.debug("[remove] arrow key was pressed, updateModel() was NOT called");
} else {
logger.debug("[remove] calling updateModel()");
updateModel();
}
clearSelection();
}
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
if (modelFilling) {
logger.debug("[insert] model is being filled now");
return;
}
// insert the string into the document
super.insertString(offs, str, a);
// if (enterKeyPressed) {
// logger.debug("[insertString] enter key was pressed");
// enterKeyPressed = false;
// return;
// }
String text = getText(0, getLength());
if (arrowKeyPressed) {
logger.debug("[insert] arrow key was pressed, updateModel() was NOT called");
model.setSelectedItem(text);
logger.debug( String.format("[insert] model.setSelectedItem(%s)", text) );
arrowKeyPressed = false;
} else if(!text.equals(getSelectedItem())){
logger.debug("[insert] calling updateModel()");
updateModel();
}
clearSelection();
}
}
public void setText(String text) {
if (model.data.contains(text)) {
setSelectedItem(text);
} else {
addToTop(text);
setSelectedIndex(0);
}
}
public String getText() {
return getEditor().getItem().toString();
}
private String previousPattern = null;
private void setPattern(String pattern) {
if(pattern!=null && pattern.trim().isEmpty())
pattern = null;
if(previousPattern==null && pattern ==null ||
pattern!=null && pattern.equals(previousPattern)) {
logger.debug("[setPatter] pattern is the same as previous: "+previousPattern);
return;
}
previousPattern = pattern;
modelFilling = true;
// logger.debug("setPattern(): start");
model.setPattern(pattern);
if(logger.isDebugEnabled()) {
StringBuilder b = new StringBuilder(100);
b.append("pattern filter '").append(pattern==null ? "null" : pattern).append("' set:\n");
for(int i=0; i list = new ArrayList(limit);
private List lowercase = new ArrayList(limit);
private List filtered;
void add(String s) {
list.add(s);
lowercase.add(s.toLowerCase());
}
void addToTop(String s) {
list.add(0, s);
lowercase.add(0, s.toLowerCase());
}
void remove(int index) {
list.remove(index);
lowercase.remove(index);
}
List getList() {
return list;
}
List getFiltered() {
if(filtered==null)
filtered = list;
return filtered;
}
int size() {
return list.size();
}
void setPattern(String pattern) {
if (pattern == null || pattern.isEmpty()) {
filtered = list;
AutoCompleteCombo.this.setSelectedItem(model.getElementAt(0));
logger.debug( String.format("[setPattern] combo.setSelectedItem(null)") );
} else {
filtered = new ArrayList(limit);
pattern = pattern.toLowerCase();
for(int i=0; isize2) {
fireIntervalRemoved(this, size2, size1-1);
fireContentsChanged(this, 0, size2-1);
}
}
public void addToTop(String aString) {
if(aString==null || data.contains(aString))
return;
if(data.size()==0)
data.add(aString);
else
data.addToTop(aString);
while(data.size()>limit) {
int index = data.size()-1;
data.remove(index);
}
setPattern(null);
model.setSelectedItem(aString);
logger.debug( String.format("[addToTop] model.setSelectedItem(%s)", aString) );
//saving into options
if (data.size() > 0) {
writeData();
}
}
@Override
public Object getSelectedItem() {
return selected;
}
@Override
public void setSelectedItem(Object anObject) {
if ((selected != null && !selected.equals(anObject)) ||
selected == null && anObject != null) {
selected = (String) anObject;
fireContentsChanged(this, -1, -1);
}
}
@Override
public int getSize() {
return data.getFiltered().size();
}
@Override
public Object getElementAt(int index) {
return data.getFiltered().get(index);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
// Logger root = Logger.getRootLogger();
// root.addAppender(new ConsoleAppender(new PatternLayout("%d{ISO8601} [%5p] %m at %l%n")));
Logger root = Logger.getRootLogger();
root.addAppender(new ConsoleAppender(new PatternLayout("%d{ISO8601} %m at %L%n")));
// BasicConfigurator.configure();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(3, 1));
final JLabel label = new JLabel("label ");
frame.add(label);
final AutoCompleteCombo combo = new AutoCompleteCombo();
// combo.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
//
// @Override
// public void keyReleased(KeyEvent e) {
// if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// String text = combo.getEditor().getItem().toString();
// if(text.isEmpty())
// return;
// combo.addToTop(text);
// }
// }
// });
frame.add(combo);
JComboBox combo2 = new JComboBox(new String[] {"Item 1", "Item 2", "Item 3", "Item 4"});
combo2.setEditable(true);
frame.add(combo2);
frame.pack();
frame.setSize( 500, frame.getHeight() );
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Java (programming language)
Opinions expressed by DZone contributors are their own.
Comments