Reputation: 13153
I want to have a dialog to specify filters for data in a JTable
. The dialog will have a second JTable
; one of the columns will represent the column in the 1st JTable
to which the filter applies; one of the other columns would be the string to use for filtering.
I've got other columns in the 2nd JTable
in the real app—whether the filter ensures the string is present or absent, etc.—but those aren't important to the problem.
I created and installed a custom renderer and editor for a TableColumn
, and registered those with the 2nd table for the TableColumn
class; I want the string representing the column in the 2nd table to be the header from the TableColumn
, and to be able to reference the actual TableColumn
as the item being edited.
In the code I have so far, when I first display the dialog with a row of fake data in it, the header value from the column in the fake data is indeed being shown. When I click on the value, however, the dropdown value for each column is the string you get when you execute tableColumn.toString()
.
My code that builds the cell editor dropdown puts TableColumn
values in the JComboBox<TableColumn>
, as I thought it would have to; how can I get the editor to use the header value from the tableColumn instead of using its toString(), or whatever it's doing?
One more thing: in the real app, the columns in the 1st JTable
are dynamic; I cannot simply list the columns in the code. I derive the number of columns and their headers from the data. Please don't just give me examples that assume that the columns to be edited are known ahead of time. If someone can explain how it works instead of just giving examples, that'd be great, but if we have to communicate entirely by examples, please include the dynamic nature of the tables.
package lincoln.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
@SuppressWarnings("serial")
public class ExampleColumnCombo extends JFrame
{
private JButton getDialogButton = null;
public static void main(String ... arguments)
{
ExampleColumnCombo me = new ExampleColumnCombo();
me.go(arguments);
}
private void go(String ... arguments)
{
createUI(this);
setVisible(true);
}
// defines one filter test: each item defines a column to test
// and the value to test in it.
class FilterItem
{
private TableColumn column = null;
private String targetString = null;
public FilterItem (TableColumn tc, String targetString)
{
column = tc;
this.targetString = targetString;
}
public TableColumn getColumn() { return column; }
public String getTargetString() { return targetString; }
}
private void createUI(final ExampleColumnCombo mainFrame)
{
Object[][] data = { { "color", "red" }, {"shape", "square"}, {"fruit", "banana"}, {"plain", "text"}};
String[] columnNames = {"type", "value" };
DefaultTableModel model = new DefaultTableModel(data, columnNames);
final JTable table = new JTable(model);
getDialogButton = new JButton("filters");
getDialogButton.addActionListener
(
new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
FilterModel filterModel = new FilterModel();
filterModel.addItem(new FilterItem(table.getColumnModel().getColumn(0), "color"));
FilterDialog dialog = new FilterDialog(mainFrame, filterModel);
dialog.setVisible(true);
}
}
);
JPanel upperPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
upperPanel.add(getDialogButton);
add(upperPanel, BorderLayout.NORTH);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300,300);
setLocationByPlatform(true);
}
class FilterModel extends AbstractTableModel
{
private ArrayList<FilterItem> filters = new ArrayList<>();
public int getColumnCount() { return 2; }
public boolean isCellEditable(int row, int column) { return true; }
public Class<?> getColumnClass(int i)
{
switch (i)
{
case 0: return TableColumn.class;
case 1: return String.class;
default: System.err.println("error, bad column given for class");
}
return null;
}
@Override
public int getRowCount() { return filters.size(); }
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
FilterItem item = filters.get(rowIndex);
switch(columnIndex)
{
case 0: return item.getColumn(); //break;
case 1: return item.getTargetString(); //break;
default: System.err.println("wrong"); new Exception().printStackTrace(); break;
}
return null;
}
public void addItem(FilterItem item)
{
filters.add(item);
}
}
class FilterDialog extends JDialog
{
public FilterDialog(ExampleColumnCombo mainDisplay,
final FilterModel filterModel)
{
JTable table = new JTable(filterModel);
table.setDefaultRenderer(TableColumn.class, new TableColumnCellRenderer());
table.setDefaultEditor(TableColumn.class, new TableColumnCellEditor());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
setSize(200,200);
pack();
setVisible(true);
}
}
class TableColumnCellEditor extends AbstractCellEditor implements
TableCellEditor, ActionListener
{
private TableColumn tableColumn;
public TableColumnCellEditor() {}
@Override
public Object getCellEditorValue()
{
return tableColumn;
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int col)
{
if (value instanceof TableColumn)
{
tableColumn = (TableColumn)value;
}
JComboBox<TableColumn> comboTableColumnType = new JComboBox<>();
Enumeration<TableColumn> columnEnumeration = table.getColumnModel().getColumns();
while (columnEnumeration.hasMoreElements())
{
TableColumn column = (TableColumn)columnEnumeration.nextElement();
comboTableColumnType.addItem(column);
}
comboTableColumnType.setSelectedItem(value);
comboTableColumnType.addActionListener(this);
return comboTableColumnType;
}
@Override
public void actionPerformed(ActionEvent e)
{
@SuppressWarnings("unchecked")
JComboBox<TableColumn> comboTableColumnType = (JComboBox<TableColumn>) e.getSource();
this.tableColumn = (TableColumn)comboTableColumnType.getSelectedItem();
}
@Override
public String toString()
{
return this.tableColumn.getHeaderValue().toString();
}
}
public class TableColumnCellRenderer extends DefaultTableCellRenderer
{
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
if (value instanceof TableColumn)
{
TableColumn tableColumn = (TableColumn) value;
Object o = tableColumn.getHeaderValue();
setText((String)o);
}
return this;
}
}
}
Upvotes: 1
Views: 629
Reputation: 205875
One approach is to use a JComboBox<String>
for the editor and populate its model with the result returned by getHeaderValue().toString()
.
JComboBox<String> comboTableColumnType = new JComboBox<>();
Enumeration<TableColumn> columnEnumeration = table.getColumnModel().getColumns();
while (columnEnumeration.hasMoreElements()) {
TableColumn column = (TableColumn) columnEnumeration.nextElement();
comboTableColumnType.addItem(column.getHeaderValue().toString());
}
There's no need to override toString()
in TableColumnCellEditor
.
Upvotes: 2