Question

I have an application similar to TableFilterDemo which produces a db with some fields similar to this

{new File("/path/with/names/included/here.file"), "foo", "bar", new Integer(5), new Double(0.7)},

The path might be very long so I have implemented a cell renderer which only shows the file name, but sets the full path as a tooltip.

table.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer(){

        @Override
        protected void setValue(Object value) {
            Object result = value;
            if ((value != null) && (value instanceof File)) {
                File file = (File) value;
                result = file.getName();
                super.setValue(result);
                super.setToolTipText(file.toString());
            }
        }

    });

The problem now is that sorting the file column still sorts by full path, not just the filename. I´m guessing I need to implement some sort of comparator, but I don't know where to start.

EDIT: To be clear, I have made a gist to show what I mean.

I want to be able to show just the filename (here.file, there.file) in the cells instead of the full path. When sorting the file column, also sort by the filename instead of the full path. The full path should be a tooltip when the mouse pointer is over the respective file cell. When filtering the table, I would like to have the full path included in the search as well.

EDIT3: SSCCE with solution

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import javax.swing.table.DefaultTableCellRenderer;

public class TableFilterDemo extends JPanel {

    private JTable table;
    private JTextField filterText;
    private TableRowSorter<MyTableModel> sorter;

    public TableFilterDemo() {
        setLayout(new BorderLayout());

        // Comparator for the file column, sorts by name instead of full path
        Comparator<File> fileComparator = new Comparator<File>() {
            @Override
            public int compare(File file1, File file2) {
                return file1.getName().compareTo(file2.getName());
            }

        };

        // Create a table with a sorter.
        MyTableModel model = new MyTableModel();
        sorter = new TableRowSorter<MyTableModel>(model);
        sorter.setComparator(0, fileComparator);



        table = new JTable(model);
        table.setRowSorter(sorter);
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);

        // For the purposes of this example, better to have a single
        // selection.
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {

            @Override
            protected void setValue(Object value) {
                Object result = value;
                if ((value != null) && (value instanceof File)) {
                    File file = (File) value;
                    result = file.getName();
                    super.setValue(result);
                    super.setToolTipText(file.toString());
                }
            }
        });
        // Add the scroll pane to this panel.
        add(
            new JScrollPane(table), BorderLayout.CENTER);

        // Create a separate form for filterText and statusText
        filterText = new JTextField();

        // Whenever filterText changes, invoke newFilter.
        filterText.getDocument().addDocumentListener(new DocumentListener() {

            public void changedUpdate(DocumentEvent e) {
                newFilter();
            }

            public void insertUpdate(DocumentEvent e) {
                newFilter();
            }

            public void removeUpdate(DocumentEvent e) {
                newFilter();
            }
        });
        add(filterText, BorderLayout.SOUTH);

    }

    /**
     * Update the row filter regular expression from the expression in the text
     * box.
     */
    private void newFilter() {
        RowFilter<MyTableModel, Object> rf = null;
        // If current expression doesn't parse, don't update.
        try {
            rf = RowFilter.regexFilter("(?i)" + filterText.getText());
        } catch (java.util.regex.PatternSyntaxException e) {
            return;
        }
        sorter.setRowFilter(rf);
    }

    class MyTableModel extends AbstractTableModel {

        private String[] columnNames = {"File", "a", "b", "c", "d"};
        private Object[][] data = {
            {new File("/long/path/with/names/included/2012-10-11-001.file"),
                "foo", "bar", new Integer(5), new Double(0.7)},
            {new File("/path/full/of/text/2012-10-11-002.file"),
                "baz", "bah", new Integer(2), new Double(1.7)},
            {new File("/a/long/path/full/of/other/text/2012-10-11-003.file"),
                "baz", "bah", new Integer(2), new Double(1.7)},};

        public int getColumnCount() {
            return columnNames.length;
        }

        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        /*
         * Don't need to implement this method unless your table's data can
         * change.
         */
        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI() {
        // Create and set up the window.
        JFrame frame = new JFrame("TableFilterDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create and set up the content pane.
        TableFilterDemo newContentPane = new TableFilterDemo();
        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();
            }
        });
    }
}
Was it helpful?

Solution

The TableRowSorter class provides the setComparator(int column, Comparator<?> comparator) method. That should do exactly what you need. Provide a Comparator that orders Files by getName(). Added to what you already have, I think that gives you what you are looking for.

OTHER TIPS

The example below shows how to display just the file name with the full path as a tool tip. You can incorporate the full path in your filter as shown here

Addendum: @Paul Hicks asks, "Can you point out the bits that are new and important?"

The essential change is to make the model, rather than the renderer, return the value that you want to display. Then let the view query the model for the detail needed to render the tool tip.

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.io.File;

/** @see https://stackoverflow.com/a/23450990/230513 */
public class TableFilterDemo extends JPanel {

    private JTable table;
    private JTextField filterText;
    private TableRowSorter<MyTableModel> sorter;

    public TableFilterDemo() {
        setLayout(new BorderLayout());

        // Create a table with a sorter.
        MyTableModel model = new MyTableModel();
        sorter = new TableRowSorter<MyTableModel>(model);
        table = new JTable(model) {

            @Override
            public String getToolTipText(MouseEvent e) {
                Point p = e.getPoint();
                int row = convertRowIndexToModel(rowAtPoint(p));
                MyTableModel model = (MyTableModel) table.getModel();
                return model.getPath(row);
            }
        };
        table.setRowSorter(sorter);
        table.setPreferredScrollableViewportSize(new Dimension(500, 200));
        table.setFillsViewportHeight(true);

        // For the purposes of this example, better to have a single
        // selection.
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // Add the scroll pane to this panel.
        add(
            new JScrollPane(table), BorderLayout.CENTER);

        // Create a separate form for filterText and statusText
        filterText = new JTextField();

        // Whenever filterText changes, invoke newFilter.
        filterText.getDocument().addDocumentListener(new DocumentListener() {

            public void changedUpdate(DocumentEvent e) {
                newFilter();
            }

            public void insertUpdate(DocumentEvent e) {
                newFilter();
            }

            public void removeUpdate(DocumentEvent e) {
                newFilter();
            }
        });
        add(filterText, BorderLayout.SOUTH);

    }

    /**
     * Update the row filter regular expression from the expression in the text
     * box.
     */
    private void newFilter() {
        RowFilter<MyTableModel, Object> rf = null;
        // If current expression doesn't parse, don't update.
        try {
            rf = RowFilter.regexFilter("(?i)" + filterText.getText());
        } catch (java.util.regex.PatternSyntaxException e) {
            return;
        }
        sorter.setRowFilter(rf);
    }

    class MyTableModel extends AbstractTableModel {

        String path = System.getProperty("user.dir");
        File[] data = new File(path).listFiles();

        private String getPath(int row) {
            return data[row].getPath();
        }

        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public int getRowCount() {
            return data.length;
        }

        @Override
        public String getColumnName(int col) {
            return "path";
        }

        @Override
        public Object getValueAt(int row, int col) {
            return data[row].getName();
        }

        @Override
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI() {
        // Create and set up the window.
        JFrame frame = new JFrame("TableFilterDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create and set up the content pane.
        TableFilterDemo newContentPane = new TableFilterDemo();
        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();
            }
        });
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top