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
Newsletter
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Java 27 Makes G1 the Default Everywhere: How to Test Your Small Containers Before You Upgrade
  • Stop Writing Database Migration Scripts: Declarative Schema Sync for Java
  • Searching Kafka Without an Index: Lessons From Building Message Search for Kafdrop
  • ScyllaDB in Enterprise Java: High-Performance NoSQL With Eclipse JNoSQL

Trending

  • DuckDB for Python Developers
  • Replay, Scheduling, and Throttling: Engineering a Self-Service Kafka Streaming Tool at Scale
  • Designing Frontend Applications for Failure, Not Just Success
  • Supercharging AI Agents with Azure Context: A Hands-On Guide to Azure MCP

Draft This article hasn't been published, so readers can't see it.

In moderation This article is waiting for review, so readers can't see it yet.

Deleted This article has been deleted, so readers can't see it.

  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
·
Jul. 25, 09 · Code Snippet
Post on X (opens in a new tab) Share on LinkedIn (opens in a new tab)
19.5K Views

Join the DZone community Save articles, join the discussion, and download Refcards and Trend Reports with a free account.

Join for free Already a member? Log in


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

  • Java 27 Makes G1 the Default Everywhere: How to Test Your Small Containers Before You Upgrade
  • Stop Writing Database Migration Scripts: Declarative Schema Sync for Java
  • Searching Kafka Without an Index: Lessons From Building Message Search for Kafdrop
  • ScyllaDB in Enterprise Java: High-Performance NoSQL With Eclipse JNoSQL

Partner Resources

×

Comments

Loading comments

The comments didn't load

Please try again in a moment.

Likes

Loading

The next likes didn't load. Please try again.

The likes didn't load

Please try again in a moment.

See who liked this

Log in or join DZone for free to see who liked this article.

Log in Join for free
  • 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