JTable unfocuses and refuses to display editor
I have a JTable in a JScrollPane that has a header row. This JTable is set to never auto resize so that it uses the preferred widths and creates a horizontal scrollbar.
I also have 2 "footer" rows. Each footer row is a seperate JTable that is placed below the JScrollPane. The footer area is placed in a JViewport so that I may sync the scrolling of the table and the footer. Each of these tables has it's own table model, but they all (including the main table) share the same column model (so that column resizing/reordering effects all of them). The first footer row is a totalling row and cannot be edited. The second row is for applying filters and can be typed in (and filters the data as you type).
The table can be seen at:
http://neicpopcorn.virtual.vps-host.net/manager/ideas/TestDataGrid.html
Okay. So the problem:
The last footer row, the one where you type to filter, has two major (probably related) problems:
1) After editing a cell, when you type Enter or Tab or anything to leave the cell, it causes the focus to shift back to the first cell. The actual behavior I can remember from other JTables I have worked with is to go on and edit the next cell.
2) If the main table is not as wide as the JScrollPane something weird problem happens when editing the filters. When you begin to edit a cell, it edits, never shows the JTextField, adds whatever letter you typed, and then shifts the focus to neverland. This is VERY annoying. You have to click with your mouse, type a letter, click, type, click, type. The focus is technically still on the JTable (I know this because I checked with KeyboardFocusManager). But no cell is focused. Also, stopCellEditing or cancelCellEditing is never called on the editor.
I have tried many things to try to fix this. Setting auto resize on the second problem is always there! There is quite a bit of code, but here are the parts I feel may be pertinent. If you need any other segments, don't hesitate to ask.
Thanks.
The FilterModel class. This is the table model for the last row. It also has a sub class that is the editor for those cells.
publicclass FilterModelextends AbstractTableModel{
GridModel gridModel;//this is the model used for the main table
String[] filters;
public FilterModel(GridModel gridModel){
this.gridModel = gridModel;
filters =new String[gridModel.getColumnCount()];
java.util.Arrays.fill(filters,"");
}
public String getColumnName(){return"";}
publicint getRowCount(){return 1;}
publicint getColumnCount(){return gridModel.getColumnCount();}
public Object getValueAt(int r,int c){
return gridModel.isCollapsed(c)?null:filters[c];
}
publicvoid setValueAt(Object o,int r,int c){
setFilter((String)o, c);
}
publicvoid setFilter(String filter,int c){
if (c == -1)return;
if (filters[c].equals(filter))return;
filters[c] = filter;
gridModel.applyFilter(filter, c);
}
publicboolean isCellEditable(int r,int c){
return !gridModel.isCollapsed(c);
}
public FilterEditor getEditor(){
returnnew FilterEditor();
}
//This is the editor used in the final row to set the filter as you type
publicclass FilterEditorextends DefaultCellEditorimplements DocumentListener{
private JTextField text;
privateint curCol = -1;
public FilterEditor(){
super(new JTextField());
text = (JTextField)editorComponent;
text.setMargin(new Insets(0,0,0,0));
text.getDocument().addDocumentListener(this);
}
public Component getTableCellEditorComponent(JTable table, Object value,boolean isSelected,int r,int c){
Component comp = super.getTableCellEditorComponent(table, value, isSelected, r, c);
curCol = table.convertColumnIndexToModel(c);
return comp;
}
publicboolean stopCellEditing(){
if (super.stopCellEditing()){
curCol = -1;
returntrue;
}
returnfalse;
}
publicvoid cancelCellEditing(){
super.cancelCellEditing();
curCol = -1;
}
publicvoid insertUpdate(DocumentEvent e){ setFilter(text.getText(), curCol);}
publicvoid removeUpdate(DocumentEvent e){ setFilter(text.getText(), curCol);}
publicvoid changedUpdate(DocumentEvent e){}
}
}
Here are excerts from the applets init() method when everything is laid out and the footer is created.
gridModel =new GridModel(url(getParameter("file")));
table =new JTable(gridModel);
table.setCellSelectionEnabled(true);
totals =new JTable(new TotalsModel(gridModel), table.getColumnModel());
totals.setCellSelectionEnabled(true);
FilterModel filterModel =new FilterModel(gridModel);
filter =new JTable(filterModel, table.getColumnModel());
filter.setCellSelectionEnabled(true);
....
filter.setDefaultEditor(Object.class, filterModel.getEditor());
....
final JScrollPane scroll =new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
table.setAutoResizeMode(table.AUTO_RESIZE_OFF);
this.getContentPane().add(scroll, BorderLayout.CENTER);
//Create the footer
JPanel footerRows =new JPanel(new BorderLayout());
footerRows.add(totals, BorderLayout.NORTH);
footerRows.add(filter, BorderLayout.SOUTH);
JPanel footerArea =new JPanel(new BorderLayout());
footerArea.add(Box.createHorizontalStrut(1), BorderLayout.WEST);
footerArea.add(footerRows, BorderLayout.CENTER);
footerArea.add(Box.createHorizontalStrut(scroll.getVerticalScrollBar().getPreferredSize().width+1), BorderLayout.EAST);
final JViewport footer =new JViewport();
footer.setView(footerArea);
//Sync the scrolling of the JScrollPane and the viewport
scroll.getViewport().addChangeListener(new ChangeListener(){
publicvoid stateChanged(ChangeEvent e){
JViewport view = (JViewport)e.getSource();
Dimension oldD = footer.getViewSize();
Dimension newD =new Dimension(view.getViewSize().width, footer.getView().getSize().height);
if (!oldD.equals(newD))
footer.setViewSize(newD);
Point oldP = footer.getViewPosition();
Point newP =new Point(view.getViewPosition().x,0);
if (!oldP.equals(newP))
footer.setViewPosition(newP);
}
});
this.getContentPane().add(footer, BorderLayout.SOUTH);
Thanks!

