4

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.

Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • not possible with the superposition of two one-dimensional selection models. You need a real two-diemsional model. There used to be an example on the old codeguru site, which required tweaks in the ui delegate. Dont know if/where it is now – kleopatra Oct 02 '11 at 09:22
  • @kleopatra: thanks. I think I'll find a different solution. – Heisenbug Oct 02 '11 at 09:34
  • can I put Bounty to your question, maybe is there another solutions, – mKorbel Oct 03 '11 at 09:46
  • @mKorbel: of course. Accordingly to kleopatra there is no possibility with the default selection model. But if you are interested to a possible solution you are free to put a bounty. It's up to you :) . – Heisenbug Oct 03 '11 at 09:48

2 Answers2

9
  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();
            }
        });
    }
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 2
    @Heisenbug I think that not possible with ListSelectionModel, because is only One_Dimensional, if works Column then doesn't works Row and vice_versa, in one of non-free solution is there AbstractTableModel that is synchronized (numbers row/column, add or remove row/column, but strict with reorderingColumn(false)) 2nd. AbstractTableModel that contains only Boolean values and in CustomRenderer (in preparedRenderer too) paint Backgroung if 2nd. TableColumn contains true, this should be direct way – mKorbel Oct 01 '11 at 19:04
7

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();
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • well, it seems not to work. I'm not able to select multiple single cells that are not contiguos this way. – Heisenbug Oct 01 '11 at 14:11
  • I'm trying it on Windows with ctrl modifier. But I'm checking for bugs now, because even @mKorbel's solution seems to work with my JTable. Maybe I have a bug somewhere else. – Heisenbug Oct 01 '11 at 14:26
  • I'm trying to extract it from my code, but it's not so simple. I have several classes involved: a builder that construct a JTable extension with custom renderers and editors, and so on.. – Heisenbug Oct 01 '11 at 14:35
  • I've suggested a potential alternative above. – trashgod Oct 02 '11 at 06:06
  • +1..thanks for the answer. But I haven't understood if I can apply such mechanism directly to a JTable? – Heisenbug Oct 02 '11 at 08:47
  • 1
    @trashgod heavens thanks!!! `list.setVisibleRowCount(N);`, now I understood whyt I missed in my code that I posted here and down-voted by our Swing Lady – mKorbel Oct 02 '11 at 09:41