4

I'm trying to create a custom cell renderer that will display an image in JTable's header cell. I've gotten the source code to work with the Metal L&F but I am encountering problems with Nimbus. Under normal circumstances, Nimbus displays the image just fine. However, when a table is sorted, Nimbus will draw the sort icon instead of the icon I've specified. This is different than the Metal L&F, as that will always draw the icon I've provided.

Example image demonstrating error in Nimbus L&F vs Metal L&F

Does anyone know of a way to have Nimbus draw the image even if a column is sorted?

I'm using Java 6.29 & Nimbus. I can't change the Java release or the L&F.

Also, I've tried to do some other workarounds, like changing the label to use HTML and and img tag to display the image, but this produces a weird visual effect. EDIT The text and image aren't aligned well (even with a HTML align tag on the img tag) Here is a picture, notice how the text in the Temp Hi column doesn't align:

example image of solution with HMTL and img tag

import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;


public class ImageChangeDemo extends JFrame {
    public static void main(String args[]) {
        //comment out the code below to try in Metal L&F
        try {
            for(javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.
                    getInstalledLookAndFeels()) {
                if("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ImageChangeDemo().setVisible(true);
            }
        });
    }

    public ImageChangeDemo(){
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        JScrollPane pane = new javax.swing.JScrollPane();
        JTable table = new javax.swing.JTable();
        table.setAutoCreateRowSorter(true);
        table.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {"a", "q", "h", "v"},
                {"b", "m", "l", "h"},
                {"d", "c", "a", "d"},
                {"j", "o", "y", "e"}
            },
            new String [] {
                "Col 1", "Col 2", "Col 3", "Col 4"
            }
        ) {
            Class[] types = new Class [] {
                String.class, String.class, String.class, String.class
            };
            @Override
            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        pane.setViewportView(table);
        this.add(pane);

        table.getTableHeader().setDefaultRenderer(new ImageRenderer(table));

        pack();
    }

    public class ImageRenderer extends DefaultTableCellRenderer{
        TableCellRenderer orig;
        ImageIcon icon;
        ImageRenderer(JTable table){
            orig = table.getTableHeader().getDefaultRenderer();
        }
        @Override
        public Component getTableCellRendererComponent(final JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component c = 
                    orig.getTableCellRendererComponent(
                        table, value, isSelected, hasFocus, row, column);
            if(c instanceof JLabel){
                if(true){
                    JLabel label = (JLabel)c;
                    label.setIcon(makeIcon());
                }
            }
            return c;
        }

        public ImageIcon makeIcon(){
            if(icon == null)
            icon = new ImageIcon(
                    ImageChangeDemo.class.getResource("/resources/green_triangle_down.png"));
            return icon;
        }
    }
}

EDIT: Here is an example scenario of what my real application should do: If the table column contains certain data (such as any strings beginning with a certain word) display a warning icon next to the column name in the table header. I've gotten this to work fine. Now, if the user sorts a column with the image, Nimbus is removing the image and replacing it with a sort icon - I still want the original warning icon to display.

Gregory Peck
  • 636
  • 7
  • 22
  • *"this produces a weird visual effect"* Weird like LSD? A (small) screenshot paints a thousand words. – Andrew Thompson Jun 06 '12 at 14:55
  • The text and image aren't aligned well (even with a HTML align tag on the img tag). I didn't include many details on this as its not my preferred way to solve the problem - in the real application many of the table headers are formatted with HTML. So adding the img tag causes lots of alignment problems in this situation. I'll update my post to include a little more details. If I get time I'll try to post various screenshots. – Gregory Peck Jun 06 '12 at 15:14
  • @Gregory Peck do you want to replace built_in icons for RowSorter with your own ??? – mKorbel Jun 06 '12 at 15:28
  • Not exactly. I'm looking to add an icon to certain columns headers based on a condition (not in my source but is part of the real program). When I sort a column, Nimbus is replacing the icon I set with the default ascending/descending icon. – Gregory Peck Jun 06 '12 at 15:31
  • @Andrew I edited the example with pictures. – Gregory Peck Jun 06 '12 at 15:43
  • this code doesn't works with Metal instance too, because replaced icons from RowSorter – mKorbel Jun 06 '12 at 15:44
  • did you tried getIcon from RowSorter f.e. getSortOrder() – mKorbel Jun 06 '12 at 15:47
  • @mKorbel Thanks for the help so far, but when I run the code it works as desired in Metal L&F. What I'm looking for is to have the default sort icons to be replaced with the icon I have provided. – Gregory Peck Jun 06 '12 at 16:00
  • @mKorbel sorry if there was any confusion, I'm not looking to customize the sort order icon, I'm looking to use my own, custom icon. In the example pictures I've posted its a down green triangle, but in the real application it will be an indicator about the data contained in the column (like a warning icon alerting the user to bad data.) – Gregory Peck Jun 06 '12 at 16:10
  • you have to replace icon, not to add Icon to the Component / JComponent / JLabel returns Renderer, I'll post short answer – mKorbel Jun 06 '12 at 16:15

2 Answers2

5
  • don't recreate an Icon inside Renderer, prepare that before, otherwise you'll recreating Icon in the crazy periods

  • not to add Icon to the Component / JComponent / JLabel returns Renderer

  • put to the Renderer

code made by Darryl or Rob

protected Icon getIcon(JTable table, int column) {
    SortKey sortKey = getSortKey(table, column);
    if (sortKey != null && table.convertColumnIndexToView(
        sortKey.getColumn()) == column) {
        switch (sortKey.getSortOrder()) {
            case ASCENDING:
                return UIManager.getIcon("Table.ascendingSortIcon");
            case DESCENDING:
                return UIManager.getIcon("Table.descendingSortIcon");
        }
    }
    return null;
}

EDIT

thank to Renderer by @trashgod, UNSORTED isn't required to override for Renderer, try & enjoy

initial view

enter image description here

ASCENDING

enter image description here

DESCENDING

enter image description here

UNSORTED

enter image description here

import java.awt.Component;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.UIManager;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class ImageChangeDemo extends JFrame {

    private static final long serialVersionUID = 1L;
    private JTable table = new javax.swing.JTable();

    public static void main(String args[]) {
        //comment out the code below to try in Metal L&F
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ImageChangeDemo().setVisible(true);
            }
        });
    }

    public ImageChangeDemo() {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        JScrollPane pane = new javax.swing.JScrollPane();



        table.setModel(new javax.swing.table.DefaultTableModel(
                new Object[][]{
                    {"a", "q", "h", "v"},
                    {"b", "m", "l", "h"},
                    {"d", "c", "a", "d"},
                    {"j", "o", "y", "e"}
                },
                new String[]{
                    "Col 1", "Col 2", "Col 3", "Col 4"
                }) {

            private static final long serialVersionUID = 1L;
            Class[] types = new Class[]{
                String.class, String.class, String.class, String.class
            };

            @Override
            public Class getColumnClass(int columnIndex) {
                return types[columnIndex];
            }
        });
        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel()) {

            @Override
            public void toggleSortOrder(int column) {
                if (column >= 0 && column < getModelWrapper().getColumnCount() && isSortable(column)) {
                    List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
                    if (!keys.isEmpty()) {
                        SortKey sortKey = keys.get(0);
                        if (sortKey.getColumn() == column && sortKey.getSortOrder() == SortOrder.DESCENDING) {
                            setSortKeys(null);
                            return;
                        }
                    }
                }
                super.toggleSortOrder(column);
            }
        };
        table.setRowSorter(sorter);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        table.setDefaultRenderer(ImageChangeDemo.class, new HeaderRenderer(table));
        pane.setViewportView(table);
        add(pane);
        pack();
    }

    class HeaderRenderer implements TableCellRenderer {

       final TableCellRenderer renderer;

        public HeaderRenderer(JTable table) {
            renderer = table.getTableHeader().getDefaultRenderer();
        }

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

        public Icon getIcon(JTable table, int column) {
            for (RowSorter.SortKey sortKey : table.getRowSorter().getSortKeys()) {
                if (sortKey.getColumn() == column) {
                    switch (sortKey.getSortOrder()) {
                        case ASCENDING:
                            return (UIManager.getIcon("Table.ascendingSortIcon"));
                        case DESCENDING:
                            return (UIManager.getIcon("Table.descendingSortIcon"));
                    }
                }
            }
            return null;
        }
    }
}

EDIT 2

then to set Icon directly to the UIManager

enter image description here

enter image description here

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.UIManager;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class ImageChangeDemo extends JFrame {

    private static final long serialVersionUID = 1L;
    private JTable table = new javax.swing.JTable();

    public static void main(String args[]) {
        //comment out the code below to try in Metal L&F
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    UIManager.getLookAndFeelDefaults().put("Table.ascendingSortIcon", new BevelArrowIcon(BevelArrowIcon.UP, false, false));
                    UIManager.getLookAndFeelDefaults().put("Table.descendingSortIcon", new BevelArrowIcon(BevelArrowIcon.DOWN, false, false));
                    break;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ImageChangeDemo().setVisible(true);
            }
        });
    }

    public ImageChangeDemo() {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        JScrollPane pane = new javax.swing.JScrollPane();
        //table.setAutoCreateRowSorter(true);
        table.setModel(new javax.swing.table.DefaultTableModel(
                new Object[][]{
                    {"a", "q", "h", "v"},
                    {"b", "m", "l", "h"},
                    {"d", "c", "a", "d"},
                    {"j", "o", "y", "e"}
                },
                new String[]{
                    "Col 1", "Col 2", "Col 3", "Col 4"
                }) {

            private static final long serialVersionUID = 1L;
            Class[] types = new Class[]{
                String.class, String.class, String.class, String.class
            };

            @Override
            public Class getColumnClass(int columnIndex) {
                return types[columnIndex];
            }
        });
        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel()) {

            @Override
            public void toggleSortOrder(int column) {
                if (column >= 0 && column < getModelWrapper().getColumnCount() && isSortable(column)) {
                    List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
                    if (!keys.isEmpty()) {

                        SortKey sortKey = keys.get(0);
                        if (sortKey.getColumn() == column && sortKey.getSortOrder() == SortOrder.DESCENDING) {
                            setSortKeys(null);
                            return;
                        }
                    }
                }
                super.toggleSortOrder(column);
            }
        };
        table.setRowSorter(sorter);
        //table.getTableHeader().setDefaultRenderer(new DefaultTableHeaderCellRenderer());
        //table.setDefaultRenderer(ImageChangeDemo.class, new HeaderRenderer(table));
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        pane.setViewportView(table);
        add(pane);
        pack();
    }

   static class BevelArrowIcon implements Icon {

        public static final int UP = 0;         // direction
        public static final int DOWN = 1;
        private static final int DEFAULT_SIZE = 11;
        private Color edge1;
        private Color edge2;
        private Color fill;
        private int size;
        private int direction;

        public BevelArrowIcon(int direction, boolean isRaisedView, boolean isPressedView) {
            if (isRaisedView) {
                if (isPressedView) {
                    init(UIManager.getColor("controlLtHighlight"), UIManager.getColor("controlDkShadow"), UIManager.getColor("controlShadow"), DEFAULT_SIZE, direction);
                } else {
                    init(UIManager.getColor("controlHighlight"), UIManager.getColor("controlShadow"), UIManager.getColor("control"), DEFAULT_SIZE, direction);
                }
            } else {
                if (isPressedView) {
                    init(UIManager.getColor("controlDkShadow"), UIManager.getColor("controlLtHighlight"), UIManager.getColor("controlShadow"), DEFAULT_SIZE, direction);
                } else {
                    init(UIManager.getColor("controlShadow"), UIManager.getColor("controlHighlight"), UIManager.getColor("control"), DEFAULT_SIZE, direction);
                }
            }
        }

        public BevelArrowIcon(Color edge1, Color edge2, Color fill, int size, int direction) {
            init(edge1, edge2, fill, size, direction);
        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            switch (direction) {
                case DOWN:
                    drawDownArrow(g, x, y);
                    break;
                case UP:
                    drawUpArrow(g, x, y);
                    break;
            }
        }

        @Override
        public int getIconWidth() {
            return size;
        }

        @Override
        public int getIconHeight() {
            return size;
        }

        private void init(Color edge1, Color edge2, Color fill, int size, int direction) {
            edge1 = Color.red;
            edge2 = Color.blue;
            this.edge1 = edge1;
            this.edge2 = edge2;
            this.fill = fill;
            this.size = size;
            this.direction = direction;
        }

        private void drawDownArrow(Graphics g, int xo, int yo) {
            g.setColor(edge1);
            g.drawLine(xo, yo, xo + size - 1, yo);
            g.drawLine(xo, yo + 1, xo + size - 3, yo + 1);
            g.setColor(edge2);
            g.drawLine(xo + size - 2, yo + 1, xo + size - 1, yo + 1);
            int x = xo + 1;
            int y = yo + 2;
            int dx = size - 6;
            while (y + 1 < yo + size) {
                g.setColor(edge1);
                g.drawLine(x, y, x + 1, y);
                g.drawLine(x, y + 1, x + 1, y + 1);
                if (0 < dx) {
                    g.setColor(fill);
                    g.drawLine(x + 2, y, x + 1 + dx, y);
                    g.drawLine(x + 2, y + 1, x + 1 + dx, y + 1);
                }
                g.setColor(edge2);
                g.drawLine(x + dx + 2, y, x + dx + 3, y);
                g.drawLine(x + dx + 2, y + 1, x + dx + 3, y + 1);
                x += 1;
                y += 2;
                dx -= 2;
            }
            g.setColor(edge1);
            g.drawLine(xo + (size / 2), yo + size - 1, xo + (size / 2), yo + size - 1);
        }

        private void drawUpArrow(Graphics g, int xo, int yo) {
            g.setColor(edge1);
            int x = xo + (size / 2);
            g.drawLine(x, yo, x, yo);
            x--;
            int y = yo + 1;
            int dx = 0;
            while (y + 3 < yo + size) {
                g.setColor(edge1);
                g.drawLine(x, y, x + 1, y);
                g.drawLine(x, y + 1, x + 1, y + 1);
                if (0 < dx) {
                    g.setColor(fill);
                    g.drawLine(x + 2, y, x + 1 + dx, y);
                    g.drawLine(x + 2, y + 1, x + 1 + dx, y + 1);
                }
                g.setColor(edge2);
                g.drawLine(x + dx + 2, y, x + dx + 3, y);
                g.drawLine(x + dx + 2, y + 1, x + dx + 3, y + 1);
                x -= 1;
                y += 2;
                dx += 2;
            }
            g.setColor(edge1);
            g.drawLine(xo, yo + size - 3, xo + 1, yo + size - 3);
            g.setColor(edge2);
            g.drawLine(xo + 2, yo + size - 2, xo + size - 1, yo + size - 2);
            g.drawLine(xo, yo + size - 1, xo + size, yo + size - 1);
        }
    }
}
Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Thanks for the help, but I don't understand where I would put this in my source code. Could you please clarify? – Gregory Peck Jun 06 '12 at 16:38
  • `case UNSORTED:`? See also this [answer](http://stackoverflow.com/a/6644956/230513). – trashgod Jun 06 '12 at 16:40
  • @mKorbel I changed the pictures in my question so maybe its a little more clear - I'm not looking to change the UI Manager's sort Icon. I'm looking to add an extra icon to the table header. – Gregory Peck Jun 06 '12 at 16:50
  • @trashgod Thanks for the link but I'm already doing that. The problem in my question is that Nimbus is replacing the icon I've set with the sort icon. – Gregory Peck Jun 06 '12 at 16:51
  • I'll edit my answer here, as @ trashgod mentioned I'll add UNSORTED (I think not required to override for Renderer in this case) and by using his code I'll post [SSCCE](http://sscce.org/) – mKorbel Jun 06 '12 at 16:53
  • 1
    @Gregory Peck just to replace `return (UIManager.getIcon("Table.ascendingSortIcon"));` with any `Icon` – mKorbel Jun 06 '12 at 17:02
  • @mKorbel Thanks but I'm still having problems setting my image. It seems like your new update only changes the sort order. I'm not interesting in sort order but instead placing a custom icon in the JTable's header row. Also, when running your new source with the debugger `getIcon()` is never called – Gregory Peck Jun 06 '12 at 17:54
  • @mKorbel I'm trying to add an image to any row header. My problem is Nimbus is erasing my custom image with the sort icon. Do you have any thoughts about how to display a custom image in any row header cell? (I do really like your BevelIcon though) – Gregory Peck Jun 06 '12 at 18:55
  • @Gregory Peck there could be another issue [Nimbus UIManager and Nimbus Defaults](http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/_nimbusDefaults.html#table) talking about Icon in funny 7x7 resolution in the pixels, isn't there your issue – mKorbel Jun 06 '12 at 19:46
  • (I think) for [Standard L&F is there TableHeaderRenderers](http://tips4java.wordpress.com/?s=DefaultTableHeaderCellRenderer) made by great minds, not sure if will be works (very lazy to test something today) under Nimbus L&F – mKorbel Jun 06 '12 at 20:52
  • Thanks so much for the help, this is a very tricky issue. What I've decided to do is create a renderer that returns a JLayeredPane containing the original Header & also the image I want to display in a higher layer. I'll post what I come up with, but it's going to take awhile. – Gregory Peck Jun 06 '12 at 21:07
  • never to use JLayeredPane for that, use [GlassPane](http://stackoverflow.com/questions/10055336/jtabbedpane-show-task-progress-in-a-tab/10059295#10059295) or maybe better would be JLayer(Java7) based on JXLayer (Java6), – mKorbel Jun 06 '12 at 21:16
  • @mKorbel Why wouldn't I want to use a JLayerpedPane for this? I thought of using the GlassPane, but I know that in the final application other classes use the glass pane & I didn't want to interfere with them. – Gregory Peck Jun 06 '12 at 21:31
  • this component still remembers dinosauruses – mKorbel Jun 06 '12 at 21:42
  • In your first example, the code for "getIcon" is never used.. so I cannot effectively change the icons. I can comment that method and it works – francogp Jul 11 '18 at 01:14
3

So after much trial and error I was able to figure out a way to have my custom icon in the header row even if the column is sorted. Basically what I did was have the renderer return a custom panel containing 2 children, the image in a JLabel and the component that was originally produced by default renderer. (Note that this workaround is only necessary for Nimbus L&F, and the original example code works fine in the Metal L&F)

This code uses StackLayout created by Romain Guy as demonstrated in his book Filthy Rich Clients - see page p245. Here is the source for StackLayout

Here is the code I created for the renderer. Make sure to download StackLayout else this won't compile.

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;


public class ImageChangeDemo extends JFrame {
    public static void main(String args[]) {
        //comment out the code below to try in Metal L&F
        try {
            for(javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.
                    getInstalledLookAndFeels()) {
                if("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ImageChangeDemo().setVisible(true);
            }
        });
    }

    public ImageChangeDemo(){
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        JScrollPane pane = new javax.swing.JScrollPane();
        JTable table = new javax.swing.JTable();
        table.setAutoCreateRowSorter(true);
        table.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {"a", "q", "h", "v"},
                {"b", "m", "l", "h"},
                {"d", "c", "a", "d"},
                {"j", "o", "y", "e"}
            },
            new String [] {
                "Col 1", "Col 2", "Col 3", "Col 4"
            }
        ) {
            Class[] types = new Class [] {
                String.class, String.class, String.class, String.class
            };
            @Override
            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        pane.setViewportView(table);
        this.add(pane);


        pack();
        //set renderer after pack so header row has correct default height
        table.getTableHeader().setDefaultRenderer(new ImageRenderer(table));


    }

    public class ImageRenderer extends DefaultTableCellRenderer{
        TableCellRenderer orig;
        private final ImageIcon icon = new ImageIcon(
                    ImageChangeDemo.class.getResource("/resources/exclamation-icon.png"));;
        private JPanel jp = new JPanel(new StackLayout());
        private final JLabel pic = new JLabel(icon);
        { //extra initialization for PIC
            pic.setHorizontalAlignment(JLabel.LEADING); //so it isn't centered in stack layout
        }

        ImageRenderer(JTable table){
            orig = table.getTableHeader().getDefaultRenderer();
        }

        @Override
        public Component getTableCellRendererComponent(final JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component c = 
                    orig.getTableCellRendererComponent(
                        table, value, isSelected, hasFocus, row, column);
            if(true){
                int width  = table.getColumnModel().getColumn(column).getWidth();
                int height = table.getTableHeader().getSize().height;
                System.out.println("height"+height);

                jp.removeAll();                //clean the JPanel

                //move text in label to the left so it isn't covered by the icon
                if(c instanceof JLabel){
                    JLabel l = (JLabel) c;
                    l.setPreferredSize(new Dimension(width, height));

                    FontMetrics fontMetrics = l.getFontMetrics(l.getFont());
                    int sizeOfSpace = fontMetrics.charWidth(' ');
                    int numSpaces = (int)Math.round(icon.getIconWidth() / (double)sizeOfSpace);
                    StringBuilder sb = new StringBuilder();
                    for(int i = 0; i < numSpaces; i++)
                        sb.append(' ');

                    //account for HTML in header messages
                    if(l.getText().toLowerCase().startsWith("<html>")){
                        l.setText(  l.getText().substring(0, "<html>".length()) +
                                    sb.toString() +
                                    l.getText().substring("<html>".length()));
                    }
                    else
                        l.setText(sb.toString()+l.getText());
                }


                //Add components to the JPanel & return it.
                jp.add(c, StackLayout.BOTTOM);  //will contain modifications for spacing.
                jp.add(pic, StackLayout.TOP);
                return jp;

            }
            else
                return c;
        }
    }
}
Gregory Peck
  • 636
  • 7
  • 22
  • `setHorizontalAlignment(CENTER)` and `setHorizontalTextPosition(LEFT)` and `setVerticalAlignment(BOTTOM)` – mKorbel Jun 07 '12 at 18:43
  • @mKorbel Thanks for the suggestion about alignment, I would have gone with this approach except that in the real application different columns will be right / left justified. I didn't want to globally change alignment just to add an image. So the solution I came up with (adding spaces to the text) is hacky & not very robust, but it allows for different alignments on the various columns. – Gregory Peck Jun 07 '12 at 19:05
  • you dirty complicating the simple things, aaaaaachhhh why don't you to set Icons to the UIManager, you can to create a 100 tables in one JFrame and each of then will have got the differens Icons, just after creating a new JTable you'll to call [UIMAnager.put(....) and then SwingUtilities.updateComponentTreeUI(mytable)](http://stackoverflow.com/a/8247527/714968), – mKorbel Jun 07 '12 at 19:15
  • I'm not to able do that the same things as with JPanel, maybe I miss something, result is in this case both JTables have got the same Icons aaaachh, eeeerrrrggghtt ***** – mKorbel Jun 07 '12 at 19:34
  • @mKorbel - that is a good suggestion I hadn't thought of. In the real version of this class, I've got the renderer in its own class with the icon as a static so there should only be one instance shared amongst all tables. In the demo code I couldn't create the icon as a static as its an inner class. The code I've supplied is a striped down solution, not the more robust one I have in my real project. – Gregory Peck Jun 07 '12 at 19:35
  • @mKorbel For my purposes (and what this will be used for) The same icon will be used throughout my application, so its not a problem to have the same icon everywhere – Gregory Peck Jun 07 '12 at 19:37
  • :-) Yeah as I'm learning Nimbus is a very tricky beast to deal with. – Gregory Peck Jun 07 '12 at 19:37
  • hmmm there is one or two Custom Look and Feels based on Nimbus, one of them declared clear access to the UIManager and Colors themes :-) – mKorbel Jun 07 '12 at 19:39