FileDocCategorySizeDatePackage
CompletableJTextField.javaAPI DocExample4952Mon Jan 09 11:02:00 GMT 2006None

CompletableJTextField.java

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.util.*;
import java.util.regex.*;

public class CompletableJTextField extends JTextField
    implements ListSelectionListener {

    Completer completer;
    JList completionList;
    DefaultListModel completionListModel;
    JScrollPane listScroller;
    JWindow listWindow;

    public CompletableJTextField (int col) {
        super (col);
        completer = new Completer();
        completionListModel = new DefaultListModel();
        completionList = new JList(completionListModel);
        completionList.setSelectionMode (ListSelectionModel.SINGLE_SELECTION);
        completionList.addListSelectionListener (this);
        listScroller =
            new JScrollPane (completionList, 
                             ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                             ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        listWindow = new JWindow();
        listWindow.getContentPane().add (listScroller);
    }

    public void addCompletion (String s) {
        completer.addCompletion (s); }

    public void removeCompletion (String s) {
        completer.removeCompletion (s); }

    public void clearCompletions (String s ) {
        completer.clearCompletions (); }

    public void valueChanged (ListSelectionEvent e) {
        if (e.getValueIsAdjusting()) { return; }
        if (completionList.getModel().getSize() == 0) {return;}
        listWindow.setVisible (false);

        /*
        String completionString = 
            (String) completionList.getSelectedValue();
        // this causes an IllegalStateException:
        // "Attempt to mutate in notification"
        setText (completionString);
        */

        final String completionString =
            (String) completionList.getSelectedValue();
        Thread worker = new Thread() {
                public void run() {
                    setText (completionString);
                }
            };
        SwingUtilities.invokeLater (worker);
    }

    /** inner class does the matching of the JTextField's
        document to completion strings kept in an ArrayList
     */
    class Completer implements DocumentListener {
        private Pattern pattern;
        private ArrayList completions;
        public Completer() {
            completions = new ArrayList();
            getDocument().addDocumentListener (this);
        }

        public void addCompletion (String s) {
            completions.add (s);
            buildAndShowPopup();
        }
        
        public void removeCompletion (String s) {
            completions.remove (s);
            buildAndShowPopup();        
        }
        
        public void clearCompletions () {
            completions.clear();
            buildPopup();
            listWindow.setVisible(false);
        }
        
        private void buildPopup() {
            completionListModel.clear();
            System.out.println ("buildPopup for " + completions.size() +
                                " completions");
            Iterator it = completions.iterator();
            pattern = Pattern.compile (getText() + ".+");
            while (it.hasNext()) {
                // check if match
                String completion = (String) it.next();
                Matcher matcher = pattern.matcher (completion);
                if (matcher.matches()) {
                    // add if match
                    System.out.println ("matched "+ completion);
                    completionListModel.add (completionListModel.getSize(),
                                             completion);
                } else {
                    System.out.println ("pattern " + 
                                        pattern.pattern() +
                                        " does not match " + 
                                        completion);
                }
            }
        }

        private void showPopup() {
            if (completionListModel.getSize() == 0) {
                listWindow.setVisible(false);
                return;
            }
            // figure out where the text field is,
            // and where its bottom left is
            java.awt.Point los = getLocationOnScreen();
            int popX = los.x;
            int popY = los.y + getHeight();
            listWindow.setLocation (popX, popY);
            listWindow.pack();
            listWindow.setVisible(true);
        }

        private void buildAndShowPopup() {
            if (getText().length() < 1)
                return;
            buildPopup();
            showPopup();
        }

        // DocumentListener implementation
        public void insertUpdate (DocumentEvent e) { buildAndShowPopup(); }
        public void removeUpdate (DocumentEvent e) { buildAndShowPopup(); }
        public void changedUpdate (DocumentEvent e) { buildAndShowPopup(); }

    }

}