1

I have an application with several custom JXTables (an extension of JTable) that are updated to reflect information stored in a database.

I would like to add components at the top of the table, above the column headers, that would allow the user to search and filter results within the table. Currently, I've tried creating a JPanel with JTextFields that are added/removed/moved and resized based on a TableColumnModelListener attached to the table, and a PropertyChangeListener attached to each TableColumn.

This is what it currently looks like:

http://i.imgur.com/7bnR36y.jpg

The problem I'm having is that I use the jgoodies jforms layout for almost everything. In this specific panel, I'm just using a BoxLayout, and the components will resize (almost) perfectly, but start pushing off the screen or disappearing once I add/remove columns.

Based on the image and what I'm wanting it to do, does anyone have a suggestion on either A) how to effectively bind the components to the location/size of each column, or B) how to customize the table's columns themselves and add additional components on top of them?

Here's the code if it helps:

    private final KTable table;
    private final ArrayList<Object> columnIds = new ArrayList<>();
    private final ArrayList<JTextField> filters = new ArrayList<>();

    private final PropertyChangeListener pcm = new PropertyChangeListener() {           
        @Override           
        public void propertyChange(PropertyChangeEvent evt) {              
            switch (evt.getPropertyName()) {
                case "width":
                case "preferredWidth":
                    setFilterColumnWidth((TableColumn)evt.getSource(),                   
                            (int)evt.getNewValue());
                    break;
                case "headerValue":
                    setFilterColumnName((TableColumn)evt.getSource(),                   
                            (String)evt.getNewValue());
                    break;
                case "visible":
                    setFilterColumnVisible((TableColumn)evt.getSource(),
                            (boolean)evt.getNewValue());
                    break;
                default:
                    System.out.println(evt.getPropertyName() + " : " + evt.getOldValue() + " | " + evt.getNewValue());
            }
        }       
    };

    public KTableFilterPanel(KTable table) {
        this.table = table;
        registerListeners();

        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        for (TableColumn column : table.getColumns()) {
            addFilterColumn(column);
        }
    }

    private void addFilterColumn(TableColumn column) {
        addFilterColumn(column, -1);
    }
    private void addFilterColumn(TableColumn column, int index) {
        Object id = column.getIdentifier();
        if (columnIds.contains(id))
            return;
        JTextField filter = new JTextField(column.getHeaderValue().toString());
        if (index == -1) {
            columnIds.add(id);
            filters.add(filter);
        }
        else {
            columnIds.add(index, id);
            filters.add(index, filter);
        }
        add(filter, filters.indexOf(filter));

        revalidate();
    }
    private void removeFilterColumn(TableColumn column) {
        Object id = column.getIdentifier();
        if (!columnIds.contains(id))
            return;

        removeFilterColumn(columnIds.indexOf(id));
    }
    private void removeFilterColumn(int index) {
        JTextField filter = filters.remove(index);
        columnIds.remove(index);

        remove(filter);

        revalidate();
    }    
    private void moveFilterColumn(int from, int to) {
        if (from == to)
            return;
        Object id = columnIds.remove(from);
        JTextField filter = filters.remove(from);
        if (id == null || filter == null)
            return;

        columnIds.add(to, id);
        filters.add(to, filter);

        remove(filter);
        add(filter, to);

        revalidate();
    }
    private void setFilterColumnVisible(TableColumn column, boolean visible) {
        Object id = column.getIdentifier();
        if (!columnIds.contains(id))
            return;
        filters.get(columnIds.indexOf(id)).setVisible(visible);

        revalidate();
    }    
    private void setFilterColumnWidth(TableColumn column, int width) {
        Object id = column.getIdentifier();
        if (!columnIds.contains(id))
            return;
        JTextField filter = filters.get(columnIds.indexOf(id));

        Dimension old = filter.getPreferredSize();
        filter.setPreferredSize(new Dimension(width - 40, old.height));

        revalidate();
    }
    private void setFilterColumnName(TableColumn column, String name) {

    }

    private void registerListeners() {
        table.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
            @Override
            public void columnAdded(TableColumnModelEvent e) {
                TableColumn column = table.getColumnModel().getColumn(e.getToIndex());
                addFilterColumn(column);
                column.addPropertyChangeListener(pcm);
            }
            @Override
            public void columnRemoved(TableColumnModelEvent e) {
                removeFilterColumn(e.getFromIndex());
            }
            @Override
            public void columnMoved(TableColumnModelEvent e) {
                moveFilterColumn(e.getFromIndex(), e.getToIndex());
            }
            @Override
            public void columnMarginChanged(ChangeEvent e) {}
            @Override
            public void columnSelectionChanged(ListSelectionEvent e) {}
        });
    }

Much appreciated!

hotforfeature
  • 2,558
  • 1
  • 16
  • 24
  • boxLayout respects the max, so you probably would need to set that as well. Another thingy: you probably don't want to react to the column's prefWidth changes (they may differ from the actual). BTW: I would write a custom LayoutManager for the header panel, actually did so for JYTable (part of Synthetica addons) - so if you have some budget, you might consider spending it :-) – kleopatra Sep 02 '13 at 06:46
  • Thanks, setting the max and ignoring the preferred property change fixed a few issues. Also, the JYTable here (http://www.jyloo.com/syntheticaaddons/screenshots/table/) in the classy screenshot is pretty close to what I'm wanting, though I have no budget. That's with a LayoutManager? – hotforfeature Sep 02 '13 at 06:56
  • please did you hear something about TableColumnModelListener – mKorbel Sep 02 '13 at 07:12
  • yes, that's basically a custom layoutManager (on the header, which holds the life components). It's an adaption of something I found in the web, search for _"Sebastian Haufe" JTableHeader_ – kleopatra Sep 02 '13 at 10:52
  • mKorbel, I don't understand your comment? @kleopatra Thanks! I think I'll look into that. If you want to post your comment as an answer, I'll accept it – hotforfeature Sep 02 '13 at 14:47
  • without the code (which I can't give you for obvious reasons) it wouldn't even be a link-only answer :-) Thanks – kleopatra Sep 02 '13 at 15:26

0 Answers0