Search code examples
javaswingjtabletooltipjtableheader

How do I add a Tool Tip generated from the Table Header to my JTable cells?


I have a JTable and I am trying to add a header tool tip that will display the string that is in the table header for each of the header cells. I can set the value of the header cell:

colModel.getColumn(currentColumn).setHeaderValue(time + "(s)");

where colModel:

TableColumnModel colModel = audioTable.getColumnModel();

enter image description here enter image description here

To do this, I've added this listener:

audioTable = new JTable(modelAudio);
    JTableHeader header = audioTable.getTableHeader();
    header.addMouseMotionListener(new MouseMotionListener() {

        @Override
        public void mouseMoved(MouseEvent e) {
            System.out.println("mouseMoved");
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseDragged(MouseEvent e) {
            System.out.println("mouseDragged");
            // TODO Auto-generated method stub

        }
    });

And I see mouseMoved being printed over and over again in between this NPE:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.synth.SynthTableHeaderUI$HeaderRenderer.getTableCellRendererComponent(Unknown Source)
at javax.swing.table.JTableHeader.getToolTipText(Unknown Source)
at javax.swing.ToolTipManager$insideTimerAction.actionPerformed(Unknown Source)
at javax.swing.Timer.fireActionPerformed(Unknown Source)
at javax.swing.Timer$DoPostEvent.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$400(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

I'm not sure how to get a longer stack trace, this is how I created the table:

modelAudio = new DefaultTableModel();
modelAudio.addColumn(" ");
audioTable = new JTable(modelAudio);

The tricky thing i am trying to do is to add column's dynamically, so i add a column each time the user populates the current column like so:

TableColumn tc = new TableColumn(modelAudio.getColumnCount());
tc.setHeaderValue(" ");
audioTable.addColumn(tc);
modelAudio.addColumn(tc);

Is the way that I am adding the columns the problem causing this NPE?


Solution

  • First of all, you haven't included enough code for me to figure out why the NullPointerException, but fortunately that's okay because you're approaching the problem the wrong way.

    You don't need to use a MouseMotionListener at all. Swing already supports a ToolTip behavior, all you need to do is get access to the cell renderer itself. Simply set the renderer to a value that will set the ToolTipText property before rendering the component.

    You haven't included enough code for me to show you how to do this in your app, but I can show you how to do it in the tutorial app from Oracle: In the initial setup, do this:

    final TableCellRenderer renderer = table.getDefaultRenderer(Object.class);
    table.setDefaultRenderer(Object.class, new TableCellRenderer() {
      @Override
      public Component getTableCellRendererComponent(JTable table,
          Object value, boolean isSelected, boolean hasFocus, int row,
          int column) {
        Component component = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        String columnHeader = table.getColumnModel().getColumn(column).getHeaderValue().toString();
        ((JComponent) component).setToolTipText(columnHeader);
        return component;
      }
    });
    

    Here's the complete code of the demo with some of the unneeded code stripped out:

    import javax.swing.*;
    import javax.swing.table.TableCellRenderer;
    
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    
    public class SimpleTableDemo extends JPanel {
    
      public SimpleTableDemo() {
        super(new GridLayout(1, 0));
    
        String[] columnNames = { "First Name", "Last Name", "Sport", "# of Years",
            "Vegetarian" };
    
        Object[][] data = {
            { "Kathy", "Smith", "Snowboarding", new Integer(5), new Boolean(false) },
            { "John", "Doe", "Rowing", new Integer(3), new Boolean(true) },
            { "Sue", "Black", "Knitting", new Integer(2), new Boolean(false) },
            { "Jane", "White", "Speed reading", new Integer(20), new Boolean(true) },
            { "Joe", "Brown", "Pool", new Integer(10), new Boolean(false) } };
    
        final JTable table = new JTable(data, columnNames);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);
    
        final TableCellRenderer renderer = table.getDefaultRenderer(Object.class);
        table.setDefaultRenderer(Object.class, new TableCellRenderer() {
          @Override
          public Component getTableCellRendererComponent(JTable table,
              Object value, boolean isSelected, boolean hasFocus, int row,
              int column) {
            Component component = renderer.getTableCellRendererComponent(table,
                value, isSelected, hasFocus, row, column);
            String columnHeader = table.getColumnModel().getColumn(column)
                .getHeaderValue().toString();
            ((JComponent) component).setToolTipText(columnHeader);
            return component;
          }
        });
    
        // Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);
    
        // Add the scroll pane to this panel.
        add(scrollPane);
      }
    
      private static void createAndShowGUI() {
        // Create and set up the window.
        JFrame frame = new JFrame("SimpleTableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
        // Create and set up the content pane.
        SimpleTableDemo newContentPane = new SimpleTableDemo();
        newContentPane.setOpaque(true); // content panes must be opaque
        frame.setContentPane(newContentPane);
    
        // Display the window.
        frame.pack();
        frame.setVisible(true);
      }
    
      public static void main(String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            createAndShowGUI();
          }
        });
      }
    }
    

    And a screenshot (obviously my mouse isn't captured in the screenshot):

    enter image description here


    You can do something similar for the ColumnHeader, by using table.getTableHeader().getDefaultRenderer():

    final TableCellRenderer header = table.getTableHeader().getDefaultRenderer();
    table.getTableHeader().setDefaultRenderer(new TableCellRenderer() {
      @Override
      public Component getTableCellRendererComponent(JTable table,
          Object value, boolean isSelected, boolean hasFocus, int row,
          int column) {
        Component tableCellRendererComponent = header.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        String columnHeader = table.getColumnModel().getColumn(column)
            .getHeaderValue().toString();
        ((JComponent) tableCellRendererComponent).setToolTipText(columnHeader);
        return tableCellRendererComponent;
      }
    });