0

I am trying to add a select all box in the column header of a JTable. I have been using the answers provided in this question for guidance: How can I put a control in the JTableHeader of a JTable?. I keep getting error messages when I try and run it. Can you see the issue? I have confirmed that all the classes are working. I think it has something to do with the line:

table.getColumnModel().getColumn(0).setHeaderRenderer(new SelectAllHeader(table, 0));

However, I am really new so I am struggling to see what I have done wrong.

package guit2;

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultCellEditor;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

public class Guit2 extends JFrame{

    //CONSTRUCTOR - for the form
    public Guit2()
    {

       DefaultTableModel model = new DefaultTableModel()
        {


           @Override
           public Class<?> getColumnClass(int column)
           {
               switch(column)
               {
               case 0: 
                   return Boolean.class;
               case 1:
                   return String.class;
               case 2: 
                   return String.class;
               case 3:
                   return String.class;
               case 4: 
                   return String.class;

               default:
                   return String.class;

               }
           }

       };

        final JTable table = new JTable();

        //The Form
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(200,200,800,300);       
        setTitle("Programming 1");
        getContentPane().setLayout(null);        
        //ADD SCROLLPANE
        JScrollPane scroll=new JScrollPane();
        scroll.setBounds(70,80,600,100);
        getContentPane().add(scroll);
        //The Table
        scroll.setViewportView(table);

       //Assign the model to table
       table.setModel(model);

       model.addColumn("Select");
       model.addColumn("Position");
       model.addColumn("Team");
       model.addColumn("Points");
       model.addColumn("Manager");

       //Set custom renderer to teams column
        table.getColumnModel().getColumn(1).setCellRenderer(new ButtonRenderer());;
        table.getColumnModel().getColumn(1).setCellEditor(new ButtonEditor(new JTextField()));

        table.getColumnModel().getColumn(0).setHeaderRenderer(new SelectAllHeader(table, 0));

       for(int i=0; i<=5;i++)
       {
           model.addRow(new Object[0]);
           model.setValueAt(false,i,0);
           model.setValueAt("Our Row" +(i+1), i, 1);
           model.setValueAt("Our Column 2", i, 2);
           model.setValueAt("Our Column 3", i, 3);
           model.setValueAt("Our Column 4", i, 4);          

       }

       //Obtain selected row
       JButton btn = new JButton("Get Selected");
       btn.addActionListener(new ActionListener() {

           @Override
           public void actionPerformed(ActionEvent arg0) {

               //Get selected row 
               for(int i=0; i<table.getRowCount(); i++)
               {
                   Boolean checked = Boolean.valueOf(table.getValueAt(i,0).toString());
                   String col = table.getValueAt(i, 1).toString();

                   //Display
                   if(checked)
                   {
                       JOptionPane.showMessageDialog(null, col);
                   }

                }
       }         
    }); 

       //ADD Button to form
       btn.setBounds(20,20,130,30);
       getContentPane().add(btn);      
    }

   public static void main(String[] args) 
    {

        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                //INITIALISE JFRAME FROM
                Guit2 form = new Guit2();
                form.setVisible(true);;   

            }
        });        
    }

}

//Button renderer class

class ButtonRenderer extends JButton implements TableCellRenderer
{
    //Constructir
    public ButtonRenderer(){
        setOpaque(true);
    }
    @Override
    public Component getTableCellRendererComponent(JTable table, Object obj, 
            boolean selected, boolean focused, int row, int col) {

            setText((obj==null) ? "":obj.toString());

        return this;
    }

}

//Button editor class

class ButtonEditor extends DefaultCellEditor
{
    protected JButton btn;
    private String lbl;
    private Boolean clicked;

    public ButtonEditor(JTextField txt){
        super(txt);

        btn=new JButton();
        btn.setOpaque(true);
        setClickCountToStart(1);

        //When button is clicked
        btn.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent e){             

                fireEditingStopped();
            }
        });
    }
    //Overide a couple of methods
    public Component getTableCellEditorComponent(JTable table, Object obj,
            boolean selected, int row, int col) {        

        //Set button text
        lbl=(obj==null) ? "":obj.toString();
        btn.setText(lbl);     
        clicked=true; 
        return btn;
    }
    //If button cell value changes (clicked)
    @Override
    public Object getCellEditorValue(){

        if(clicked)
        {
            JOptionPane.showMessageDialog(btn, lbl+" Clicked");
        }
        clicked = false;
      return new String(lbl);    
    }

    @Override
    public boolean stopCellEditing(){

       clicked=false;
       return super.stopCellEditing();
    }

    @Override
    protected void fireEditingStopped(){
        super.fireEditingStopped();
    }  

}

/**
 * A TableCellRenderer that selects all or none of a Boolean column.
 * 
 * @param targetColumn the Boolean column to manage
 * @see https://stackoverflow.com/a/7137801/230513
 */

class SelectAllHeader extends JToggleButton implements TableCellRenderer {

    private static final String ALL = "✓ Select all";
    private static final String NONE = "✓ Select none";
    private JTable table;
    private TableModel tableModel;
    private JTableHeader header;
    private TableColumnModel tcm;
    private int targetColumn;
    private int viewColumn;

    public SelectAllHeader(JTable table, int targetColumn) {
        super(ALL);
        this.table = table;
        this.tableModel = table.getModel();
        if (tableModel.getColumnClass(targetColumn) != Boolean.class) {
            throw new IllegalArgumentException("Boolean column required.");
        }
        this.targetColumn = targetColumn;
        this.header = table.getTableHeader();
        this.tcm = table.getColumnModel();
        this.applyUI();
        this.addItemListener(new ItemHandler());
        header.addMouseListener(new MouseHandler());
        tableModel.addTableModelListener(new ModelHandler());
    }

    @Override
    public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected,
        boolean hasFocus, int row, int column) {
        return this;
    }

    private class ItemHandler implements ItemListener {

        @Override
        public void itemStateChanged(ItemEvent e) {
            boolean state = e.getStateChange() == ItemEvent.SELECTED;
            setText((state) ? NONE : ALL);
            for (int r = 0; r < table.getRowCount(); r++) {
                table.setValueAt(state, r, viewColumn);
            }
        }
    }

    @Override
    public void updateUI() {
        super.updateUI();
        applyUI();
    }

    private void applyUI() {
        this.setFont(UIManager.getFont("TableHeader.font"));
        this.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
        this.setBackground(UIManager.getColor("TableHeader.background"));
        this.setForeground(UIManager.getColor("TableHeader.foreground"));
    }

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mouseClicked(MouseEvent e) {
            viewColumn = header.columnAtPoint(e.getPoint());
            int modelColumn = tcm.getColumn(viewColumn).getModelIndex();
            if (modelColumn == targetColumn) {
                doClick();
            }
        }
    }

    private class ModelHandler implements TableModelListener {

        @Override
        public void tableChanged(TableModelEvent e) {
            if (needsToggle()) {
                doClick();
                header.repaint();
            }
        }
    }

    // Return true if this toggle needs to match the model.
    private boolean needsToggle() {
        boolean allTrue = true;
        boolean allFalse = true;
        for (int r = 0; r < tableModel.getRowCount(); r++) {
            boolean b = (Boolean) tableModel.getValueAt(r, targetColumn);
            allTrue &= b;
            allFalse &= !b;
        }
        return allTrue && !isSelected() || allFalse && isSelected();
    }
}

The error message reads:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at guit2.SelectAllHeader.needsToggle(Guit2.java:316)
    at guit2.SelectAllHeader.access$800(Guit2.java:229)
    at guit2.SelectAllHeader$ModelHandler.tableChanged(Guit2.java:304)
    at javax.swing.table.AbstractTableModel.fireTableChanged(AbstractTableModel.java:296)
    at javax.swing.table.AbstractTableModel.fireTableRowsInserted(AbstractTableModel.java:231)
    at javax.swing.table.DefaultTableModel.insertRow(DefaultTableModel.java:376)
    at javax.swing.table.DefaultTableModel.addRow(DefaultTableModel.java:350)
    at javax.swing.table.DefaultTableModel.addRow(DefaultTableModel.java:361)
    at guit2.Guit2.<init>(Guit2.java:93)
    at guit2.Guit2$3.run(Guit2.java:138)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
BUILD SUCCESSFUL (total time: 5 seconds)
Community
  • 1
  • 1
volican
  • 137
  • 1
  • 14
  • The content of the error message would probably be a really helpful addition to this question. – JJF Dec 09 '15 at 17:48
  • By fluke I actually fixed this issue. If anyone else ever wants to reuse this code then you will need to move the line table.getColumnModel().getColumn(0).setHeaderRenderer(new SelectAllHeader(table, 0)); to after the for loop. – volican Dec 09 '15 at 20:53
  • @volican: Note that citation is _required_ by the [*Stack Exchange Network Terms of Service*](http://stackexchange.com/legal). – trashgod Dec 09 '15 at 23:26

0 Answers0