6

Are there any event that is fired when cell is about to be selected? There is ListSelectionListener, but it has event that is fired only after selection has happened. I need some way to cancel selection event and using ListSelectionListener it is not easy as selection has already happened and I need to have some state variable that indicates if selection is normal or is cancel of a previous selection.

Are there a way to switch off selection notifications? However this is not 100% good solution (there will be problems if some listeners saves selection state in its local storage) this is better than nothing.

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.JTable;

public class JTableExample extends JFrame {

    /**
     * 
     */
    private static final long serialVersionUID = 6040280633406589974L;
    private JPanel contentPane;
    private JTable table;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JTableExample frame = new JTableExample();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public JTableExample() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        table = new JTable(new MyTableModel());
        ListSelectionModel selectionModel = table.getSelectionModel();
        selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        selectionModel.addListSelectionListener(new MySelectionListener());
        contentPane.add(table, BorderLayout.CENTER);
    }

    class MyTableModel extends AbstractTableModel {
        /**
         * 
         */
        private static final long serialVersionUID = -8312320171325776638L;

        public int getRowCount() {
            return 10;
        }

        public int getColumnCount() {
            return 10;
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            return rowIndex * columnIndex;
        }
    }

    class MySelectionListener implements ListSelectionListener {
        public void valueChanged(ListSelectionEvent e) {
            int selectedRow = table.getSelectedRow();
            if (selectedRow == 5) {
                System.out.println("I would like this selection never happened.");
            }
        }
    }

}
michael nesterenko
  • 14,222
  • 25
  • 114
  • 182
  • How about just deselecting that cell in your cell selection listener rather than trying to prevent the selection altogether? – Nate W. Oct 29 '11 at 00:18
  • same question as always: why? – kleopatra Oct 29 '11 at 09:51
  • @kleopatra, I need to cancel selection change. I have selection mode to set to single and user is forbidden to change row if he has not accepted change he made in that row. – michael nesterenko Oct 29 '11 at 11:12
  • "user is forbidden to change row [selection]" != "need to cancel selection change" :-) The first part is your requirement, the second is an - suboptimal - idea about how to implement the requirement. See my answer, the VetoableListSelectionModel is exactly what you need to fulfil your _requirement_ – kleopatra Oct 29 '11 at 12:40

3 Answers3

6

whatever the goal is that you want to achieve: thinking "mouseEvent" is not enough, selection might change for other reasons (f.i. keyboard input, programmatic trigger, ..). Reverting an unwated change in a listener is not an option: as you already noted that would require to keep a duplicate of the selection and might confuse other listeners.

The only way (that I see, could be others, of course ;-) is not to let it happen in the first place: implement a List SelectionModel which doesn't change the selection if certain conditions are met. My favourite (biased me :-) is a VetoableListSelectionModel It's a subclass of DefaultListSelectionModel which in SingleSelectionMode waits for vetoes from interested parties before actually changing.

Here's a (raw) code snippet using it:

    VetoableListSelectionModel vetoableSelection = new VetoableListSelectionModel();
    VetoableChangeListener navigationController = new VetoableChangeListener() {

        public void vetoableChange(PropertyChangeEvent evt)
                throws PropertyVetoException {
            // custom method that implements your condition 
            if (!canSelect((int) evt.getOldValue(), (int) evt.getNewValue()))
                throw new PropertyVetoException("uncommitted changes",
                        evt);
        }

    };
    vetoableSelection.addVetoableChangeListener(navigationController);
    myTable.setSelectionModel(vetoableSelection);
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • When I show a YES-NO dialogue inside `vetoableChange`, then the ListSelectionListener is not called with `evt.getValueIsAdjusting()==true`. I guess problem is, that the mouseReleased is not fired. – xmedeko Nov 12 '13 at 12:24
  • @xmedeko hmm ... don't quite understand your scenario, though might be interested: consider posting a question with an SSCCE demonstrating the problem :-) – kleopatra Nov 13 '13 at 11:04
1

The only way to do this that i can think of is handle the MouseEvent and using MouseAdapters, get the coordinates and somehow to check whether the mouse pointer is hovering over a cell or not, if it is, do what you want to do. you probably have to do addMouseListener to get the effect.

Aman
  • 548
  • 1
  • 4
  • 11
  • But how can I cancel click event? – michael nesterenko Oct 29 '11 at 06:49
  • well, that's the way JTables work, the cells and rows and selectable and clickable. You can a) remove the ListSelectionListener implementation so that clicks or selects are never handled or b) make the JTable disabled, which would essentially prevent any user interactions with the grid and hence your event would never fire. That's all I can think of... – Aman Oct 29 '11 at 08:14
1

Assuming you have set the selection mode of the ListSelectionModel to the desired value and added a listener, you may find it helpful to examine the predicate getValueIsAdjusting(), which "Returns true if the selection is undergoing a series of changes." In practical terms, it is true when the mouse is down, or when it is being dragged in one of the INTERVAL modes.

It may also help to know more about the goal of the this effort, as another approach may be helpful. Naturally, an sscce is always in order.

Addendum: This appears to work, but @kleopatra's approach would prevent losing the previous selection.

private static final int FORBID = 5;
class MySelectionListener implements ListSelectionListener {

    public void valueChanged(ListSelectionEvent e) {
        int selectedRow = table.getSelectedRow();
        if (selectedRow == FORBID) {
            selectionModel.removeIndexInterval(FORBID, FORBID);
            System.out.println(FORBID + " forbidden.");
        }
    }
}

user is forbidden to change row if he has not accepted change he made in that row.

You could use a custom CellEditor that conditions stopCellEditing(), as shown in this example.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I have updated question with code example. When new row index equals to 5 I want selection not to change and stay on the same row as it is. – michael nesterenko Oct 29 '11 at 07:34
  • 1
    from his/her comment below the question I assume that not loosing the previous is exactly the problem :-) Disallow navigating-away sounds like a typical validation scenario to me – kleopatra Oct 29 '11 at 14:08
  • @kleopatra: On review, I see your point. Perhaps a `CellEditor` would be appropriate? – trashgod Oct 29 '11 at 14:48
  • depends on exact requirement: if you regard the row as a "condensed form" then you might want to allow temporary invalid cell input as long as the user doesn't navigate to another row. The user experience is better if she is forced only if absolutely necessary :-) – kleopatra Oct 29 '11 at 15:09