DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Getting Started With Agentic Workflows in Java and Quarkus
  • Building AI-Powered Java Applications With Jakarta EE and LangChain4j
  • Alternative Structured Concurrency
  • Jakarta EE 12: Entering the Data Age of Enterprise Java

Trending

  • Why AI-Generated Code Breaks Your Testing Assumptions
  • AWS Managed Database Observability: Monitoring DynamoDB, ElastiCache, and Redshift Beyond CloudWatch
  • You Don't Get to Retrofit Trust: Why API Security Must Be Designed In, Not Bolted On
  • Building a Production-Ready AI Agent in 2026: Beyond the Hello World Demo
  1. DZone
  2. Coding
  3. Java
  4. Autocomplete Combobox In Java With Filtering And Inserting New Text

Autocomplete Combobox In Java With Filtering And Inserting New Text

By 
Snippets Manager user avatar
Snippets Manager
·
Jul. 25, 09 · Code Snippet
Likes (0)
Comment
Save
Tweet
Share
19.3K Views

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.

Related

  • Getting Started With Agentic Workflows in Java and Quarkus
  • Building AI-Powered Java Applications With Jakarta EE and LangChain4j
  • Alternative Structured Concurrency
  • Jakarta EE 12: Entering the Data Age of Enterprise Java

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook