Squirvin
Squirvin

Reputation: 129

Searching JComboBoxes with multi-word elements

Let's say, for example, that I have a JComboBox with the elements {"example 1", "example 2", "example 3"} (please note the spaces between example and the corresponding number).

When you try to search "example 2" by typing while the combobox is selected, it closes because the space bar toggles the popup of the component.

This could be split up into two questions:

  1. I have made a swing event and so far it recognizes the space key and I have disabled the default space bar action of the JComboBox. How do I make it so that pressing the space bar will actually make it add " " to the search?
  2. If #1 isn't possible or is unknown, what other way is there to do this?

Anyone who can answer this question properly will most definitely recieve an upvote.

Upvotes: 1

Views: 776

Answers (2)

camickr
camickr

Reputation: 324118

You can use:

KeyStroke space = KeyStroke.getKeyStroke("pressed SPACE");
InputMap im = comboBox.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.getParent().remove(space);

The problem with this solution is that it removes the popup functionality for all combo boxes in your application, which may or may not be what you want.

Edit:

If this is for a single combo box the more work required. You need to invoke the comboBox.selectWithKeyChar() method. This is easy enough to do with a custom Action. Unfortunately this still doesn't work because this code ends up invoking the DefaultKeySelectionManager which depends on some class variables contained in the BasicComboBoxUI class. Therefore I ended up writing my own ow KeySelectionManager which keeps all these variable local. This is what I came up with:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import javax.swing.text.*;

public class ComboBoxKeySelection extends JPanel
{
    JComboBox<String> comboBox;

    public ComboBoxKeySelection()
    {
        String[] data =
        {
            " 1", " 2",  " 3", " 4",
            "a", "ab", "abc", "abcd",
            "b1", "b2", "b3", "b4", "be",
            "c", "d", "e", "f"
        };

        comboBox = new JComboBox<String>( data );
        add( comboBox );

        Action search = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                comboBox.selectWithKeyChar( " ".charAt(0) );
            }
        };

        KeyStroke space = KeyStroke.getKeyStroke("typed SPACE");
        comboBox.getActionMap().put("spacePopup", search);

        comboBox.setKeySelectionManager( new MyKeySelectionManager(comboBox) );
    }


    static class MyKeySelectionManager implements JComboBox.KeySelectionManager
    {
        private JComboBox comboBox;
        private JList listBox;
        private boolean useComboBoxModel;

        private long timeFactor;
        private long lastTime;
        private long time;

        private String prefix = "";
        private String typedString = "";

        public MyKeySelectionManager(JComboBox comboBox)
        {
            this.comboBox = comboBox;

            Long l = (Long)UIManager.get("ComboBox.timeFactor");
            timeFactor = l == null ? 1000L : l.longValue();

            Object child = comboBox.getAccessibleContext().getAccessibleChild(0);

            if (child instanceof BasicComboPopup)
            {
                BasicComboPopup popup = (BasicComboPopup)child;
                listBox = popup.getList();
                useComboBoxModel = false;
            }
            else
            {
                listBox = new JList();
                useComboBoxModel = true;
            }
        }

        public int selectionForKey(char aKey, ComboBoxModel aModel)
        {
            if (useComboBoxModel)
            {
                listBox.setModel( aModel );
            }

            time = System.currentTimeMillis();
            boolean startingFromSelection = true;
            int startIndex = comboBox.getSelectedIndex();

            if (time - lastTime < timeFactor)
            {
                typedString += aKey;

                if((prefix.length() == 1) && (aKey == prefix.charAt(0)))
                {
                    // Subsequent same key presses move the keyboard focus to the next
                    // object that starts with the same letter.
                    startIndex++;
                }
                else
                {
                    prefix = typedString;
                }
            }
            else
            {
                startIndex++;
                typedString = "" + aKey;
                prefix = typedString;
            }

            lastTime = time;

            if (startIndex < 0 || startIndex >= aModel.getSize())
            {
                startingFromSelection = false;
                startIndex = 0;
            }

            int index = listBox.getNextMatch(prefix, startIndex, Position.Bias.Forward);

            if (index < 0 && startingFromSelection)
            {
                // wrap
                index = listBox.getNextMatch(prefix, 0, Position.Bias.Forward);
            }

            return index;
        }
    }


    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("ComboBoxKeySelection");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new ComboBoxKeySelection() );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

Upvotes: 4

Daan Luttik
Daan Luttik

Reputation: 2855

you could catch the keypressed event and simply append the " " at the desired string.

Upvotes: 0

Related Questions