Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtain currently highlighted item from JComboBox popup (not selected item)

I'd like to be able to react when currently highlighted item in a JComboBox drop down list changes. Note that I'm not looking for a way to get the currently selected item, but the highlighted one. When mouse hovers over this popup it highlights the item at mouse position, but this does not affect currently selected item, so I cannot simply listen via an ItemListener or ActionListener to achieve what I want.

I'm trying to create a component which consists of a JComboBox and a coupled tooltip which displays additional information (documentation) for the currently highlighted item.

As my first attempt I'm adding some code to my constructor (extended JComboBox):

import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleState;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.ComboPopup;

public class SomeFrame extends JFrame {

    private MyComboBox combo;

    public SomeFrame() {
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setSize(100,20);
        setLocationRelativeTo(null);
        setLayout(new BorderLayout());
        combo = new MyComboBox();        
        combo.setModel(new DefaultComboBoxModel(new String[]{"one", "two", "three", "four"}));
        add(combo);
        pack();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                SomeFrame frame = new SomeFrame();
                frame.setVisible(true);
            }
        });
    }    

    // this is the important part
    private static class MyComboBox extends JComboBox {

        public MyComboBox() {
            getAccessibleContext().addPropertyChangeListener(new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    if (AccessibleContext.ACCESSIBLE_STATE_PROPERTY.equals(evt.getPropertyName())
                            && AccessibleState.FOCUSED.equals(evt.getNewValue())
                            && getAccessibleContext().getAccessibleChild(0) instanceof ComboPopup) {
                        ComboPopup popup = (ComboPopup) getAccessibleContext().getAccessibleChild(0);
                        JList list = popup.getList();
                        System.out.println("--> " + String.valueOf(list.getSelectedValue()));
                    }
                }
            });
        }
    }

}

It seems to work, but I got to this code through some shady channels and trial/error, so I'm thinking there has to be a better way of doing it. Any ideas? Is above code even production safe?

like image 820
predi Avatar asked Apr 10 '13 14:04

predi


People also ask

Which event gets generated when you select an item from a JComboBox?

The combo box fires an action event when the user selects an item from the combo box's menu.

Which is the description of a JComboBox?

JComboBox is a part of Java Swing package. JComboBox inherits JComponent class . JComboBox shows a popup menu that shows a list and the user can select a option from that specified list . JComboBox can be editable or read- only depending on the choice of the programmer .


2 Answers

Good question and good solution - except that there seems to be bug in accessibleCombo which doesn't update its internal wiring on updateUI, that is, when toggling the LAF:

  • the accessible selection changes of the list are fired by an internal ListSelectionListener registered to the comboPopup list
  • the comboPopup is controlled by the ui-delegate and re/created in installUI
  • accessibleCombo doesn't update its internal list to the newly created and installed

Not much you can do about it. So I would listen directly to the list selection, then you have full control about re-wiring on LAF changes:

public static class XComboBox extends JComboBox {

    private ListSelectionListener listener;

    public XComboBox() {
        uninstall();
        install();
    }

    @Override
    public void updateUI() {
        uninstall();
        super.updateUI();
        install();
    }

    private void uninstall() {
        if (listener == null) return;
        getPopupList().removeListSelectionListener(listener);
        listener = null;
    }

    protected void install() {
        listener = new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) return;

                JList list = getPopupList();
                System.out.println("--> " + String.valueOf(list.getSelectedValue()));
            }
        };
        getPopupList().addListSelectionListener(listener);
    }

    private JList getPopupList() {
        ComboPopup popup = (ComboPopup) getUI().getAccessibleChild(this, 0);
        return popup.getList();

    }
}
like image 184
kleopatra Avatar answered Nov 15 '22 08:11

kleopatra


I'm trying to create a component which consists of a JComboBox and a coupled tooltip

Create a custom renderer for the combo box. Then in the renderer use the setToolTipText(...) method.

The section Specifying Tool Tips For Cells from the JTable tutorial shows how to do this for tables. The concept should be the same for a comboBox renderer.

like image 44
camickr Avatar answered Nov 15 '22 08:11

camickr