3

I found this code to make a header component on JTable, but I'm unable to give the same border to the header as of the default one. Following is the screenshot of the code:

screenshot

Here Select all checkbox doesn't have the same border as Column 1 and Column 2 so it looks different. And also it's level is somewhat low as compared to the up in Columns n.

Source code is as follows:-

package com.test.check.table;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.*;

public class SelectAllHeaderTest {

    private static final int BOOLEAN_COL = 2;
    private static final Object colNames[] = {"Column 1", "Column 2", ""};
    private DefaultTableModel model = new DefaultTableModel(null, colNames) {

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            if (columnIndex == BOOLEAN_COL) {
                return Boolean.class;
            } else {
                return String.class;
            }
        }
    };
    private JTable table = new JTable(model);

    public void create() {
        for (int x = 1; x < 6; x++) {
            model.addRow(new Object[]{
                    "Row " + x + ", Col 1", "Row " + x + ", Col 2", false
                });
        }
        table.setAutoCreateRowSorter(true);
        table.setPreferredScrollableViewportSize(new Dimension(320, 160));
        TableColumn tc = table.getColumnModel().getColumn(BOOLEAN_COL);
        tc.setHeaderRenderer(new SelectAllHeader(table, BOOLEAN_COL));
        String newLookAndFeel = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
        try {
            UIManager.setLookAndFeel(newLookAndFeel);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        JFrame f = new JFrame();
        f.add(new JScrollPane(table));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new SelectAllHeaderTest().create();
            }
        });
    }
}

/**
 * A TableCellRenderer that selects all or none of a Boolean column.
 * 
 * @param targetColumn the Boolean column to manage
 */
class SelectAllHeader extends JCheckBox 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();
    }
}

From the above code I tried to give borders to the checkbox header but it didn't look like expected (upper and beweled border).

Any suggestions to make this upper will be greatly appreciated.

Thanks

Community
  • 1
  • 1
user1709952
  • 169
  • 2
  • 7
  • 21

2 Answers2

1

you change, set the L&F on runtime, part of Swing Object is created but RootPane isn't refreshed with a new L&F (Metal to Windows)

start with

  • put L&F to main class, to public static void main(String[] args) { before code line SwingUtilities.invokeLater(new Runnable() {

  • then you (code talking about) stays with JToggleButton in JTableHeader

    a) leave that, will be better and nicer

    b) remove backgroung for JToggleButton or to set opacity, change Borders for undecorated JToggleButton (imitating cell in JTableHeader)

  • original code posted and screenshot by @trashgod is from Mac OSX and there is used own L&F

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 1
    Yes, that's why I used a `JToggleButton` instead of a `JCheckBox` in the [original](http://stackoverflow.com/a/7137801/230513). – trashgod Nov 19 '13 at 15:36
  • 1
    @trashgod very nice thread, then is very easily is*** (or word is*** there, English is my favority language) remember, search for author :-) – mKorbel Nov 19 '13 at 15:50
0

As suggested, I set the border for the header like

In the checkbox renderer. I modified it like below:-

header.setBorder(BorderFactory.createRaisedBevelBorder());

Also put my L&F code in static main method before runnable.

It shows the border in the correct way and my checkbox looks fine.

Thanks a lot for your code! At last it works fine.

user1709952
  • 169
  • 2
  • 7
  • 21