Search code examples
javaswingswingxjxtreetable

TableColumnExt not respecting prototype value


I'm trying to use SwingX's TableColumnExt class to set a prototype value for the column's width in the hierarchical column of a JXTreeTable. After I initialize the model and table I do this:

TableColumnExt column = dataTable.getColumnExt(0);            
column.setPrototypeValue(500);

When my table is rendered, all columns are equally sized. These are the methods I'm using on the JXTreeTable instance.

dataTable.setRowHeight(28);
dataTable.setFillsViewportHeight(true);
dataTable.setHorizontalScrollEnabled(true);

What am I doing wrong here?


Solution

  • As already mentioned in my comments, it's a bug. The issues are many-fold:

    • the code responsible for measuring the prototype size requirements is ColumnFactory.calcPrototypeWidth
    • it's doing so by configuring the renderer as returned by the table with the given prototype and measuring its prefSize
    • the rendering of the hierarchical column is delegated to a JTree, using black magic
    • the default factory is unaware of that black magic, thus querying the JTree's prefSize which is independent of the prototype
    • the first idea: make the factory aware of the special rendering of the hierarchical column and walk down to the real - underlying treeCellRenderer - doesn't work, again due to some other internal black magic
    • automatic column re-sizing due to setting the prototype is broken in JXTable (Issue #1510)

    A workaround involves

    • a custom ColumnFactory which is aware of the special renderer (== JXTree) of the hierarchical column)
    • for the hierarchical column, configure and measure a TreeCellRenderer - not the one used by the tree itself, but a unrelated dummy
    • after setting the prototype, manually trigger a size evaluation of the column

    Below is a custom ColumnFactory and its usage (not formally tested, so take it with a grain of salt :-).

    // a custom factory
    ColumnFactory factory = new ColumnFactory() {
    
        @Override
        protected int calcPrototypeWidth(JXTable table,
                TableColumnExt columnExt) {
            if (isHierarchicalPrototype(table, columnExt))  {
                return calcHierarchicalPrototypeWidth((JXTreeTable) table, columnExt);
            }
            return super.calcPrototypeWidth(table, columnExt);
        }
    
        protected boolean isHierarchicalPrototype(JXTable table,
                TableColumnExt columnExt) {
            return (table instanceof JXTreeTable) 
                    && ((JXTreeTable) table).getTreeTableModel().getHierarchicalColumn() == 
                             columnExt.getModelIndex()
                    && columnExt.getPrototypeValue() != null;
        }
    
        TreeCellRenderer dummy = new DefaultTreeCellRenderer();
        protected int calcHierarchicalPrototypeWidth(JXTreeTable table,
                TableColumnExt columnExt) {
            JXTree renderer = (JXTree) getCellRenderer(table, columnExt);
            // commented lines would be the obvious step down into the "real" sizing
            // requirements, but giving reasonable result due to internal black magic
            // TreeCellRenderer treeRenderer = renderer.getCellRenderer();
            // Component comp = treeRenderer.getTreeCellRendererComponent(renderer, 
                  columnExt.getPrototypeValue(), false, false, false, -1, false);
            // instead, measure a dummy
            Component comp = dummy.getTreeCellRendererComponent(renderer, 
                    columnExt.getPrototypeValue(), false, false, false, -1, false);
    
            return Math.max(renderer.getPreferredSize().width, comp.getPreferredSize().width);
        }
    
    };
    
    // usage: first create the treeTable, set the factory and set the model
    JXTreeTable table = new JXTreeTable();
    table.setColumnFactory(factory);
    table.setTreeTableModel(new FileSystemModel());
    // set the prototype
    table.getColumnExt(0).setPrototypeValue("long longer longest still not enough to really see" +
               " some effect of the prototype if available");
    // Issue #1510: prototype value handling broken in underlying JXTable
    // need to manually force the config
    table.getColumnFactory().configureColumnWidths(table, table.getColumnExt(0));