Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent JList from making selection outside cell bounds?

Tags:

java

swing

jlist

"Is there any way to prevent a JList from selecting the last element when the user clicks below it on the list?"

That is the question someone asked here and I have the same problem. That guy found a so-so solution (by overriding processMouseEvent() ) but I want to know if there is a better/more elegant way to do it.

[Edit]

Ok, more detail. If you have a JList and there is some space unoccupied by any cell/element and you click that space then the last element in the JList got selected.

For a real example try this JList Swing Tutorial example, click the white space and see that Rollo got selected.

like image 222
Alex Avatar asked Jun 21 '12 12:06

Alex


People also ask

Which method is used to change the selection mode of the JList?

The selection mode can be changed on the selection model directly, or via JList 's cover method.

Which method of a JList returns if no item in the list is selected?

getAccessibleSelectionCount. Returns the number of items currently selected. If no items are selected, the return value will be 0.

How do I remove an item from JList?

To actually remove the item, we use the syntax . remove(int); where the integer is the index of the item you wish to remove. That is how we add and remove things from a JList, and this concludes our tutorial on the JList.


2 Answers

See https://forums.oracle.com/forums/thread.jspa?threadID=2206996

import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;

public class TestJList {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JList list = new JList(new Object[] { "One", "Two", "Three" }) {
                    @Override
                    public int locationToIndex(Point location) {
                        int index = super.locationToIndex(location);
                        if (index != -1 && !getCellBounds(index, index).contains(location)) {
                            return -1;
                        }
                        else {
                            return index;
                        }
                    }
                };

                list.addMouseListener(new MouseAdapter() {

                    @Override
                    public void mouseClicked(MouseEvent e) {
                        JList list = (JList) e.getSource();
                        if (list.locationToIndex(e.getPoint()) == -1 && !e.isShiftDown()
                                && !isMenuShortcutKeyDown(e)) {
                            list.clearSelection();
                        }
                    }

                    private boolean isMenuShortcutKeyDown(InputEvent event) {
                        return (event.getModifiers() & Toolkit.getDefaultToolkit()
                                .getMenuShortcutKeyMask()) != 0;
                    }
                });

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(list));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
like image 72
Walter Laan Avatar answered Sep 22 '22 16:09

Walter Laan


Looking at the call stack, you can't really do what you want without messing with AWT-Events:

Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 384 in DefaultListSelectionModel)) 
    DefaultListSelectionModel.changeSelection(int, int, int, int, boolean) line: 384    
    DefaultListSelectionModel.changeSelection(int, int, int, int) line: 415 
    DefaultListSelectionModel.setSelectionInterval(int, int) line: 459  
    TestJList$1(JList<E>).setSelectionInterval(int, int) line: 2067 
    BasicListUI$Handler.adjustSelection(MouseEvent) line: 2739  
    BasicListUI$Handler.mousePressed(MouseEvent) line: 2695 
    AWTEventMulticaster.mousePressed(MouseEvent) line: 280  
    TestJList$1(Component).processMouseEvent(MouseEvent) line: 6502 
    TestJList$1(JComponent).processMouseEvent(MouseEvent) line: 3321    
    TestJList$1.processMouseEvent(MouseEvent) line: 24  
    TestJList$1(Component).processEvent(AWTEvent) line: 6270    
    TestJList$1(Container).processEvent(AWTEvent) line: 2229    
    TestJList$1(Component).dispatchEventImpl(AWTEvent) line: 4861   
    TestJList$1(Container).dispatchEventImpl(AWTEvent) line: 2287   
    TestJList$1(Component).dispatchEvent(AWTEvent) line: 4687   
    LightweightDispatcher.retargetMouseEvent(Component, int, MouseEvent) line: 4832 
    LightweightDispatcher.processMouseEvent(MouseEvent) line: 4489  
    LightweightDispatcher.dispatchEvent(AWTEvent) line: 4422    
    JFrame(Container).dispatchEventImpl(AWTEvent) line: 2273    
    JFrame(Window).dispatchEventImpl(AWTEvent) line: 2713   
    JFrame(Component).dispatchEvent(AWTEvent) line: 4687    
    EventQueue.dispatchEventImpl(AWTEvent, Object) line: 707    
    EventQueue.access$000(EventQueue, AWTEvent, Object) line: 101   
    EventQueue$3.run() line: 666    
    EventQueue$3.run() line: 664    
    AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]    
    ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 76    
    ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext) line: 87  
    EventQueue$4.run() line: 680    
    EventQueue$4.run() line: 678    

You could implement your own ListUI and then do whatever you want (including preventing this unwanted behaviour), but I would really not recommend to go down that path.

For what it's worth (it is done by overriding processXXXEvent methods), here is a small snippet that I actually find not that ugly and that prevents selecting objects when clicking outside their bounds:

import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class TestJList {

    private JList list;

    protected void initUI() {
        JFrame frame = new JFrame("test");
        list = new JList(new Object[] { "Hello", "World", "!" }) {

            private boolean processEvent(MouseEvent e) {
                int index = list.locationToIndex(e.getPoint());
                return index > -1 && list.getCellBounds(index, index).contains(e.getPoint());
            }

            @Override
            protected void processMouseEvent(MouseEvent e) {
                if (processEvent(e)) {
                    super.processMouseEvent(e);
                }
            }

            @Override
            protected void processMouseMotionEvent(MouseEvent e) {
                if (processEvent(e)) {
                    super.processMouseMotionEvent(e);
                }
            }
        };
        list.setVisibleRowCount(10);

        frame.add(new JScrollPane(list));
        frame.pack();
        frame.setVisible(true);
    }

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

            @Override
            public void run() {
                new TestJList().initUI();
            }
        });
    }
}
like image 36
Guillaume Polet Avatar answered Sep 19 '22 16:09

Guillaume Polet