0

I'd like to have a table where people can freely select cells.

For this I've used the ListSelectionModel.MULTIPLE_INTERVAL_SELECTION and table.setCellSelectionEnabled(true).

I have 2 problems:

  • The method table.addRowSelectionInterval doesn't select any cells
  • If you select 2 blocks, the cells having the same rows and columns selected are also selected. e.g. select B2-D4 and then F7. This will also select B7-D7 and F2-F4.

Here is the program to reproduce these problems:

import java.awt.BorderLayout;
import javax.swing.*;

public class JTableCellSelection {
    public static void showDemo(JComponent demo, String title) {
        JFrame mainFrame = new JFrame();
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.setTitle(title);
        JPanel contentPanel = new JPanel(new BorderLayout());

        contentPanel.add(demo);

        mainFrame.add(contentPanel);
        mainFrame.pack();
        mainFrame.setVisible(true);
    }

    public static void main(String[] args) {
        JTable table = new JTable(10, 10);
        table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        table.setCellSelectionEnabled(true);
        table.addRowSelectionInterval(6, 7); // Select 2 lines
        showDemo(new JScrollPane(table), "Select a block and some rows");
    }
}

It feels like I will have to check the selection by myself and not rely on the isSelected of the table cell renderer.

Anthony
  • 1,245
  • 1
  • 16
  • 15
  • 2
    multiple _cell_ selection is not support. Actually, it can't with the core selection mechanism: the row/column selection are achieved by the superposition of two one-dimensional models while you would need a true two-dimensional model for cell selection. Implementing that is possible but requires deep internal changes in the ui-delegate – kleopatra Mar 22 '13 at 10:39
  • 1
    `JList` may be an alternative in the sense shown [here](http://stackoverflow.com/a/7620726/230513). – trashgod Mar 22 '13 at 10:49
  • @kleopatra I'm think in overriding the method _public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)_ and _addRowSelectionInterval_ in order to know which cells are selected. Then check the selection in the cell renderer. This would avoid chaging the ui-delegate. – Anthony Mar 22 '13 at 11:18
  • I think that will turn out to not be good enough - good luck anyway :-) – kleopatra Mar 22 '13 at 11:22
  • use XxxRenderer (painting standard selection) and to store index of selected cell in the XxxXxxModel, a few question about that here, you can to use the similair logics for your idea, – mKorbel Mar 22 '13 at 11:25

2 Answers2

1

So I've implemented it in the JTable.

Note that there are several drawbacks to this solution:

  • You cannot use the table.getListSelectionModel to select rows, you need to call table.addRowSelectionInterval
  • trying to select another column next to a selected block will unselect the rows
  • I haven't tested column selection but my guess is that it wouldn't work
  • changing direction when selecting block does not work always

but for the rest it does pretty much what I wanted

/*
 * Copyright 2013 Japplis.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.awt.Point;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.JTable;
import javax.swing.table.TableModel;

/**
 * The JTable used to display data.
 * This class is only to fix bugs or improve existing functionalities.
 *
 * @author Anthony Goubard - Japplis
 */
public class SheetTable extends JTable {

    private Map<Integer, Set<Integer>> selectedCells = new HashMap<>();
    private Point firstExtendCell;

    public SheetTable(TableModel tableModel) {
        super(tableModel);
    }

    @Override
    public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
        if (toggle && isCellSelected(rowIndex, columnIndex) && !extend) {
            selectedCells.get(rowIndex).remove(columnIndex);
        } else {
            if (!toggle && !extend) {
                selectedCells.clear();
            }
            Set<Integer> selectedColumns = selectedCells.get(rowIndex);
            if (selectedColumns == null) {
                selectedColumns = new TreeSet<>();
                selectedCells.put(rowIndex, selectedColumns);
            }
            selectedColumns.add(columnIndex);
            if (!extend) {
                firstExtendCell = new Point(rowIndex, columnIndex);
            } else {
                for (int i = Math.min(firstExtendCell.x, rowIndex); i <= Math.max(firstExtendCell.x, rowIndex); i++) {
                    for (int j = Math.min(firstExtendCell.y, columnIndex); j <= Math.max(firstExtendCell.y, columnIndex); j++) {
                        selectedCells.get(i).add(j);
                    }
                }
            }
        }
        super.changeSelection(rowIndex, columnIndex, toggle, extend);
    }

    @Override
    public void addRowSelectionInterval(int index0, int index1) {
        for (int i = index0; i < index1; i++) {
            selectedCells.remove(i);
        }
        super.addRowSelectionInterval(index0, index1);
    }

    @Override
    public void removeRowSelectionInterval(int index0, int index1) {
        for (int i = index0; i < index1; i++) {
            selectedCells.remove(i);
        }
        super.removeRowSelectionInterval(index0, index1);
    }

    @Override
    public void selectAll() {
        selectedCells.clear();
        super.selectAll();
    }

    @Override
    public void clearSelection() {
        if (selectedCells != null) {
            selectedCells.clear();
        }
        super.clearSelection();
    }

    @Override
    public boolean isCellSelected(int row, int column) {
        if (!getSelectionModel().isSelectedIndex(row)) {
            return false;
        }
        if (getSelectionModel().isSelectedIndex(row) && selectedCells.get(row) == null) {
            return true;
        }
        return selectedCells.get(row).contains(column);
    }
}

The last version is in Joeffice Mercurial repository on bitbucket.org

Anthony
  • 1,245
  • 1
  • 16
  • 15
0

I did some experiments and also read the documentation here. It says the setCellSelectionEnabled() method:

Sets whether this table allows both a column selection and a row selection to exist simultaneously. When set, the table treats the intersection of the row and column selection models as the selected cells.

I added two lines of code for the debugging purpose immediately before showDemo() to see what the selected rows and columns are after adding the rows interval:

public static void main(String[] args) {
    JTable table = new JTable(10, 10);
    table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

    table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    table.setCellSelectionEnabled(true);
    table.addRowSelectionInterval(6, 7); // Select 2 lines

    // Let's see what rows and columns have been selected 

    System.out.println("Rows: " + Arrays.toString(table.getSelectedRows()));
    System.out.print("Columns: " + Arrays.toString(table.getSelectedColumns()));

    showDemo(new JScrollPane(table), "Select a block and some rows");
}

and I got this output:

Rows: [6, 7]
Columns: []

which explains why addRowSelectionInterval doesn't work as you expected. It's easy to add the missing part:

    ...
    table.addRowSelectionInterval(6, 7); // Select 2 lines
    table.addColumnSelectionInterval(0, 9); // and Select ** ALL** the columns
    ...

Similar justification could be made for the other observation.

RGO
  • 4,586
  • 3
  • 26
  • 40