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!

[11022 byte] By [thaimina] at [2007-10-3 4:15:19]
# 1

I figured it out on my own.

From this article http://forum.java.sun.com/thread.jspa?forumID=31&threadID=411506 I kind of thought that sharing table column models would be good. But through thorough testing I found that to be the culprite. That thread does end with an example hos to sync using listeners but does not copmlete it and it still requires sharing the column model. I finished the the syncing and now can sync completely seperate table column models.

Here is the method:

public void syncColumnModel(JTable src, final JTable dest) {

src.getColumnModel().addColumnModelListener(new TableColumnModelListener() {

public void columnAdded(TableColumnModelEvent e) { } //never happens

public void columnRemoved(TableColumnModelEvent e) { } //never happens

public void columnMoved(TableColumnModelEvent e) {

dest.moveColumn(e.getFromIndex(), e.getToIndex());

}

public void columnMarginChanged(ChangeEvent e) {

TableColumnModel src = (TableColumnModel)e.getSource();

for (int i = 0; i < src.getColumnCount(); i++) {

int width = src.getColumn(i).getWidth();

TableColumn destCol = dest.getColumnModel().getColumn(i);

if (width != destCol.getWidth()) {

destCol.setPreferredWidth(width);

destCol.setWidth(width);

}

}

}

public void columnSelectionChanged(ListSelectionEvent e) { } //dont care

});

}

Call it like this: syncColumnModel(tableWithHeader, tableThatNeedsToSyncUp);

thaimina at 2007-7-14 22:16:47 > top of Java-index,Desktop,Core GUI APIs...
# 2
I took away the Test Data Grid that was linked in the first post because it was displaying sensitive information.
thaimina at 2007-7-14 22:16:47 > top of Java-index,Desktop,Core GUI APIs...