Individual and not continuous JTable's cell selection

Is there any clean way to allow a user to select multiple non continuos cells of a JTable? Or I'm forced to implement my own ListSelectionModel?

I played around with setCellSelectionEnabled() and setSelectionModel() methods on JTable but I can only select groups of continuous cells.

EDIT:

I tried @mKorbel nice SSCCE. It works fine for list but it seems not fully working on tables. Here's an SSCCE:

import java.awt.Component;

import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;


public class TableSelection extends JFrame{
    String[] columnNames = {"First Name",
            "Last Name",
            "Sport",
            "# of Years",
            "Vegetarian"};
    Object[][] data = {
            {"Kathy", "Smith",
             "Snowboarding", new Integer(5), new Boolean(false)},
            {"John", "Doe",
             "Rowing", new Integer(3), new Boolean(true)},
            {"Sue", "Black",
             "Knitting", new Integer(2), new Boolean(false)},
            {"Jane", "White",
             "Speed reading", new Integer(20), new Boolean(true)},
            {"Joe", "Brown",
             "Pool", new Integer(10), new Boolean(false)}
        };

    public TableSelection(){
        JPanel main= new JPanel();
        JTable table = new JTable(data, columnNames){
             @Override
                protected void processMouseEvent(MouseEvent e) {
                    int modifiers = e.getModifiers() | InputEvent.CTRL_MASK;
                    // change the modifiers to believe that control key is down
                    int modifiersEx = e.getModifiersEx() | InputEvent.CTRL_MASK;
                    // can I use this anywhere?  I don't see how to change the modifiersEx of the MouseEvent
                    MouseEvent myME = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), modifiers, e.getX(),
                            e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton());
                    super.processMouseEvent(myME);
                }

        };
        JScrollPane pane = new JScrollPane(table);
        main.add(pane);
        this.add(main);

        this.setSize(800, 600);
        this.setVisible(true);
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new TableSelection();
    }

}

I can select non-contiguous row but not single cells. I mean, I would like to be able to select cell 0,0 and 3,3 for example.


Solution 1:

  1. If isn't defined for JTable#setSelectionMode(ListSelectionModel.SINGLE_SELECTION), then CTRL + MOUSE_CLICK

  2. Or do you mean remember last selected?

  3. ListSelectionModel is used by both JTable and JList.

enter image description hereenter image description hereenter image description hereenter image description hereenter image description here

import java.awt.Component;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class Ctrl_Down_JList {

    private static void createAndShowUI() {
        String[] items = {"Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"};
        JList myJList = new JList(items) {

            private static final long serialVersionUID = 1L;

            @Override
            protected void processMouseEvent(MouseEvent e) {
                int modifiers = e.getModifiers() | InputEvent.CTRL_MASK;
                // change the modifiers to believe that control key is down
                int modifiersEx = e.getModifiersEx() | InputEvent.CTRL_MASK;
                // can I use this anywhere?  I don't see how to change the modifiersEx of the MouseEvent
                MouseEvent myME = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), modifiers, e.getX(),
                        e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton());
                super.processMouseEvent(myME);
            }
        };
        JFrame frame = new JFrame("Ctrl_Down_JList");
        frame.getContentPane().add(new JScrollPane(myJList));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowUI();
            }
        });
    }

Solution 2:

Use MULTIPLE_INTERVAL_SELECTION, shown in How to Use Tables: User Selections.

Addendum: Because the MULTIPLE_INTERVAL_SELECTION of ListSelectionModel is also available to JList, you may be able to leverage the latter's HORIZONTAL_WRAP to get non-contiguous selection, as shown below.

enter image description here

Console:

[Cell:06]
[Cell:06, Cell:16]
[Cell:06, Cell:16, Cell:18]
[Cell:06, Cell:08, Cell:16, Cell:18]

Code:

import java.awt.*;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.event.*;

/**
 * @see http://stackoverflow.com/questions/7620579
 * @see http://stackoverflow.com/questions/4176343
 */
public class ListPanel extends JPanel {

    private static final int N = 5;
    private DefaultListModel dlm = new DefaultListModel();
    private JList list = new JList(dlm);

    public ListPanel() {
        super(new GridLayout());
        for (int i = 0; i < N * N; i++) {
            String name = "Cell:" + String.format("%02d", i);
            dlm.addElement(name);            
        }
        list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        list.setVisibleRowCount(N);
        list.setCellRenderer(new ListRenderer());
        list.addListSelectionListener(new SelectionHandler());
        this.add(list);
    }

    private class ListRenderer extends DefaultListCellRenderer {

        public ListRenderer() {
            this.setBorder(BorderFactory.createLineBorder(Color.red));
        }

        @Override
        public Component getListCellRendererComponent(JList list, Object
            value, int index, boolean isSelected, boolean cellHasFocus) {
            JComponent jc =  (JComponent) super.getListCellRendererComponent(
                list, value, index, isSelected, cellHasFocus);
            jc.setBorder(BorderFactory.createEmptyBorder(N, N, N, N));
            return jc;
        }
    }

    private class SelectionHandler implements ListSelectionListener {

        @Override
        public void valueChanged(ListSelectionEvent e) {
            if (!e.getValueIsAdjusting()) {
                System.out.println(Arrays.toString(list.getSelectedValues()));
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("ListPanel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

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

            @Override
            public void run() {
                new ListPanel().display();
            }
        });
    }
}