title: Multiple row selection with checkbox in JTable
body: <p>I have a JTable with checkbox as one of the column. I also have a checkbox in header to check/uncheck all.
  AFAIK the default behaviour of JTable is it deselects all the rows selected before if select a new row. But can we achieve CTRL click like behavior with checkbox. That retaining the previously selected row.
  The main problem I am facing is while enabling multiple selection of JTable rows using checkbox.</p>
  <h2>Expected output</h2>
  <p>1st row is checked then first row is selected and if third row is checked then third is selected along with first row (which is already checked and selected)</p>
  <h2>Actual output</h2>
  <p>When first row is checked and selected and if select the third row then it deselects all the rows previously selected and the third one is only selected.</p>
  <p>I have a sample code which simulates the scenario I want to achieve same as the <kbd>Add Another One</kbd> button does, but with checkbox selection.</p>
  <pre><code>import javax.swing.*;
  import javax.swing.event.ListSelectionEvent;
  import javax.swing.table.AbstractTableModel;
  import java.awt.*;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import javax.swing.event.ListSelectionListener;
  import javax.swing.table.TableColumn;
  import javax.swing.event.CellEditorListener;
  
  public class JTableRowSelectProgramatically extends JPanel {
  
  final JTable table = new JTable(new MyTableModel());
  
  public JTableRowSelectProgramatically() {
      initializePanel();
  }
  
  private void initializePanel() {
      setLayout(new BorderLayout());
      setPreferredSize(new Dimension(475, 150));
  
  
      table.setFillsViewportHeight(true);
      JScrollPane pane = new JScrollPane(table);
  
      JLabel label2 = new JLabel(&quot;Row: &quot;);
      final JTextField field2 = new JTextField(3);
      JButton add = new JButton(&quot;Select&quot;);
  
      table.setRowSelectionAllowed(true);
      table.setColumnSelectionAllowed(false);
      table.getSelectionModel().addListSelectionListener(new ListSelectionListenerImpl());
      TableColumn tc = table.getColumnModel().getColumn(3);
      tc.setCellEditor(table.getDefaultEditor(Boolean.class));
      tc.setCellRenderer(table.getDefaultRenderer(Boolean.class));
      ((JComponent) table.getDefaultRenderer(Boolean.class)).setOpaque(true);
      tc.getCellEditor().addCellEditorListener(new CellEditorListenerImpl());
  
      add.addActionListener(new ActionListener() {
  
          public void actionPerformed(ActionEvent event) {
              int index2 = 0;
              try {
                  index2 = Integer.valueOf(field2.getText());
              } catch (NumberFormatException e) {
                  e.printStackTrace();
              }
              table.addRowSelectionInterval(index2, index2);
              field2.setText(String.valueOf(index2));
          }
      });
  
      JPanel command = new JPanel(new FlowLayout());
      command.add(label2);
      command.add(field2);
      command.add(add);
  
      add(pane, BorderLayout.CENTER);
      add(command, BorderLayout.SOUTH);
  }
  
  public static void showFrame() {
      JPanel panel = new JTableRowSelectProgramatically();
      panel.setOpaque(true);
  
      JFrame frame = new JFrame(&quot;JTable Row Selection&quot;);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setContentPane(panel);
      frame.pack();
      frame.setVisible(true);
  }
  
  public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
  
          public void run() {
              JTableRowSelectProgramatically.showFrame();
          }
      });
  }
  
  public class MyTableModel extends AbstractTableModel {
  
      private String[] columns = {&quot;ID&quot;, &quot;NAME&quot;, &quot;AGE&quot;, &quot;A STUDENT?&quot;};
      private Object[][] data = {
          {1, &quot;Alice&quot;, 20, new Boolean(false)},
          {2, &quot;Bob&quot;, 10, new Boolean(false)},
          {3, &quot;Carol&quot;, 15, new Boolean(false)},
          {4, &quot;Mallory&quot;, 25, new Boolean(false)}
      };
  
      public int getRowCount() {
          return data.length;
      }
  
      public int getColumnCount() {
          return columns.length;
      }
  
      public Object getValueAt(int rowIndex, int columnIndex) {
          return data[rowIndex][columnIndex];
      }
  
      @Override
      public String getColumnName(int column) {
          return columns[column];
      }
  
      @Override
      public boolean isCellEditable(int rowIndex, int columnIndex) {
          return columnIndex == 3;
      }
  
      //
      // This method is used by the JTable to define the default
      // renderer or editor for each cell. For example if you have
      // a boolean data it will be rendered as a check box. A
      // number value is right aligned.
      //
      @Override
      public Class&lt;?&gt; getColumnClass(int columnIndex) {
          return data[0][columnIndex].getClass();
      }
  
      @Override
      public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
          if (columnIndex == 3) {
              data[rowIndex][columnIndex] = aValue;
              fireTableCellUpdated(rowIndex, columnIndex);
          }
      }
  }
  
  class ListSelectionListenerImpl implements ListSelectionListener {
  
      public void valueChanged(ListSelectionEvent lse) {
          ListSelectionModel lsm = (ListSelectionModel) lse.getSource();
          int row = table.getRowCount();
          if (lsm.isSelectionEmpty()) {
          } else {
  //                If any column is clicked other than checkbox then do normal selection
  //                i.e select the click row and deselects the previous selection
              if (table.getSelectedColumn() != 3) {
                  for (int i = 0; i &lt; row; i++) {
                      if (lsm.isSelectedIndex(i)) {
                          table.setValueAt(true, i, 3);
                      } else {
                          table.setValueAt(false, i, 3);
                      }
                  }
  
              }
          }
      }
    }
  public class CellEditorListenerImpl implements CellEditorListener{
  
      public void editingStopped(ChangeEvent e) {
          for(int i=0; i&lt;table.getRowCount();i++){
              if((Boolean)table.getValueAt(i, 3)){
                  table.addRowSelectionInterval(i, i);
              }
              else{
                  table.removeRowSelectionInterval(i, i);
              }
          }
      }
  
      public void editingCanceled(ChangeEvent e) {
          System.out.println(&quot;do nothing&quot;);
      }
  
  }
  }
  </code></pre>