4

I have a JTable using a custom DefaultTableModel which has some Booleans in the last column (displayed as tick boxes).

When I add a MouseListener to retrieve the value of what's been clicked it appears that the toggling of tick boxes no longer takes place.

// As soon as this is used in the component
// that is using the JTable, the toggling stops
table.addMouseListener(new MouseAdapter() {  
            public void mouseClicked(MouseEvent evt) {  
                int col = table.getSelectedColumn();  
                int row = table.getSelectedRow();

                Object o = table.getModel().getValueAt(row, col);

I assume that the event is being consumed by the listener. What can I add to the MouseListener code to restore the toggling behaviour?

Edit:

Oops, it appears that the issue lies with my override:

@Override
public void setValueAt(Object aValue, int row, int column) {

    // Attempt at mutually exclusive checkboxes
    if( column == 2 ){ // Starts at 0. Seek an alternative solution to avoid hardcoding?
        // Algorithm: cycle through List to set other Booleans to false

        // Uses entities. Is there another way of getting the number of rows from model?
        List<MyEntity> myEntities = this.getDatas();

        for( int i = 0; i < myEntities.size(); i++ ){
            if( i != row ){
                // Make sure this calls parent
                super.setValueAt( false , i, 2);
            }
        }

    } else {
        super.setValueAt(aValue, row, column); // Call parent class
    }

}  
James P.
  • 19,313
  • 27
  • 97
  • 155
  • why you need to retrieve the value from the TableCell, for real answer to your question you have to post http://sscce.org/ – mKorbel Oct 27 '11 at 18:01
  • @mKorbel: I'd love to post some concise code but there's a few dependencies. Here is the flow behind what I want to achieve: click on column representing booleans -> set clicked boolean to true (checkbox checked) -> set all other booleans in the column to false for mutual exclusion -> if one of the checkboxes is checked, enable a JButton. – James P. Oct 27 '11 at 18:06
  • not sure what you really needed to implements, be sure the is possible one very easy way and one-two most complex ways, but which one is correct for your MVC model??? – mKorbel Oct 27 '11 at 18:12
  • I can't really explain the flow in any other way. The table model actually contains entities objects, each of them being parsed to a row in the model (each object field = one element in the table). The booleans are added on top of this data to make it possible for the user to select one and only one (mutual exclusion) of the rows/objects. – James P. Oct 27 '11 at 18:26
  • 1
    I just want to exclude a possibility that there is JToolTip, JPopup some InputMask, some verificator, whatever other, please see half answer from very simple SSCCE (great job by @trashgod), maybe http://stackoverflow.com/questions/7045851/jtable-how-prepareeditor-works – mKorbel Oct 27 '11 at 20:36
  • Whats a "tick box"? I'm not familiar with that Swing component. – camickr Oct 27 '11 at 20:40
  • @camickr: Tick box is an everyday synonym for check box (`JCheckBox` in Swing). Other environments might have an X instead of the V-style tick. @mKorbel: There are no tool tips, popups, input masks or input verifiers. – James P. Oct 27 '11 at 21:17
  • This is a Swing question, so use the proper component name so everybody knows what your are talking about and we don't have to guess. – camickr Oct 28 '11 at 00:11
  • @camickr: Sure. The question title was edited in anticipation of your remark ;) – James P. Oct 28 '11 at 01:28

1 Answers1

13

Don't add your own MouseListener. Instead override setValueAt() in the TableModel to see the new value set by the default editor for Boolean.class.

Addendum: Here's an sscce. For expedience, it simply clears all entries in CHECK_COL, sets the new value and conditions the button accordingly.

CheckOne image

import java.awt.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

/**
 * @see http://stackoverflow.com/questions/7920068
 * @see http://stackoverflow.com/questions/4526779
 */
public class CheckOne extends JPanel {

    private static final int CHECK_COL = 1;
    private static final Object[][] DATA = {
        {"One", Boolean.FALSE}, {"Two", Boolean.FALSE},
        {"Three", Boolean.FALSE}, {"Four", Boolean.FALSE},
        {"Five", Boolean.FALSE}, {"Six", Boolean.FALSE},
        {"Seven", Boolean.FALSE}, {"Eight", Boolean.FALSE},
        {"Nine", Boolean.FALSE}, {"Ten", Boolean.FALSE}};
    private static final String[] COLUMNS = {"Number", "CheckBox"};
    private DataModel dataModel = new DataModel(DATA, COLUMNS);
    private JTable table = new JTable(dataModel);
    private ControlPanel cp = new ControlPanel();

    public CheckOne() {
        super(new BorderLayout());
        this.add(new JScrollPane(table));
        this.add(cp, BorderLayout.SOUTH);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setPreferredScrollableViewportSize(new Dimension(250, 175));
    }

    private class DataModel extends DefaultTableModel {

        public DataModel(Object[][] data, Object[] columnNames) {
            super(data, columnNames);
        }

        @Override
        public void setValueAt(Object aValue, int row, int col) {
            if (col == CHECK_COL) {
                for (int r = 0; r < getRowCount(); r++) {
                    super.setValueAt(false, r, CHECK_COL);
                }
            }
            super.setValueAt(aValue, row, col);
            cp.button.setEnabled(any());
        }

        private boolean any() {
            boolean result = false;
            for (int r = 0; r < getRowCount(); r++) {
                Boolean b = (Boolean) getValueAt(r, CHECK_COL);
                result |= b;
            }
            return result;
        }

        @Override
        public Class<?> getColumnClass(int col) {
            if (col == CHECK_COL) {
                return getValueAt(0, CHECK_COL).getClass();
            }
            return super.getColumnClass(col);
        }

        @Override
        public boolean isCellEditable(int row, int col) {
            return col == CHECK_COL;
        }
    }

    private class ControlPanel extends JPanel {

        JButton button = new JButton("Button");

        public ControlPanel() {
            button.setEnabled(false);
            this.add(new JLabel("Selection:"));
            this.add(button);
        }
    }

    private static void createAndShowUI() {
        JFrame frame = new JFrame("CheckOne");
        frame.add(new CheckOne());
        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
  • It just so happens that I've overridden `setValueAt()`. There must be a flaw in my logic though. What I want to do is to have only one checkbox activated at a time or pass `setValueAt()` to my GenericTableModel. The reason I want to use a `MouseListener` is because I want to activate a JButton if one tickbox is checked (one of the Booleans is true). – James P. Oct 27 '11 at 18:00
  • An approach using `JRadioButton` is shown [here](http://stackoverflow.com/a/11259671/230513). – trashgod Dec 15 '15 at 11:10