views:

1672

answers:

8

I am working with an extension of the DefaultTableModel as follows:

This is the NEW AchievementTableModel after updating it to reflect input from some answers.

public AchievementTableModel(Object[][] c, Object[] co) {
 super(c,co);
}
public boolean isCellEditable(int r, int c) {return false;}
public void replace(Object[][] c, Object[] co) {
 setDataVector(convertToVector(c), convertToVector(co));
 fireTableDataChanged();
}

My GUI is a JTable that has the following properties:

if(table==null)
 table = new JTable(model);
else
 table.setModel(model);
table.setFillsViewportHeight(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getColumnModel().setColumnSelectionAllowed(false);

I have a JComboBox that selects which data to display. The TableModel is updated with a call to model.replace(cells) and then runs through the above table creation code again.

When selecting a row in the GUI JTable, and printing the table.getSelectedRow() value, I ALWAYS get -1 after changing the table data with a model.replace(cells) call from the first selection, even if I reselect the first JComboBox option. Is there a reason for this that I'm missing? Should I change some code?

EDIT: The code has changed a lot over trying to answer this question so here is the updated code. The new AchievementTableModel is above.

This sets up the model and table to be viewed correctly and displayed in a ScrollPane

if(model==null)
 model = new AchievementTableModel(cells, columns);
else
 model.replace(cells, columns);
if(table==null) {
 table = new JTable(model);
 table.setFillsViewportHeight(true);
 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
 table.getTableHeader().setReorderingAllowed(false);
 table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
 table.getColumnModel().setColumnSelectionAllowed(false);
 table.getTableHeader().setResizingAllowed(false);
} else
 table.setModel(model);

column = table.getColumn(columns[0]);
column.setPreferredWidth(25);
column = table.getColumn(columns[1]);
column.setPreferredWidth(225);
column = table.getColumn(columns[2]);
column.setPreferredWidth(40);
table.doLayout();

add(new JScrollPane(table), BorderLayout.CENTER);
A: 

When you reorder a JTable you need to keep track of the original indexes in the TableModel for your data, not the current indexes on the JTable. Visually the table may have shifted but the underlying data model has not.

Gandalf
Unless I'm mistaken, the TableModel is changed when I change all the data cells, fireTableDataChanged() is called and I create a new JTable with the updated model.
Kevin Stich
do you create a new JTable or just reset the model?
akf
I call model.replace(cells) and immediately after call table = new JTable(model)
Kevin Stich
See below, I don't want this behavior.
Kevin Stich
+1  A: 

When it swaps out the data its removing the selection (since the index is now different), you'll need recalculate the selection and set it programmatically.

I'll point out that in my experience, this is why I tend to extend AbstractTableModel or out right implement my own TableModel interface from the ground up. Modifying the backing data reference as here, always causes a million problems IMHO.

Petriborg
I'm selecting again after the data is swapped with the replace(cells) call, so I don't think this is the issue.
Kevin Stich
You're selecting them again as a user after the replace(cells) call, and it still returns an index of -1?
Petriborg
It returns the correct index only before I call replace, but after will always return -1, even after selecting many different rows in the JTable
Kevin Stich
"Modifying the backing data reference as here, always causes a million problems IMHO." +1, me too
instanceofTom
I would do this, but it seems like there is a lot of behind the scenes work in the transition from the AbstractTableModel to the DefaultTableModel. Could you tell me which parts would need to be changed and/or how? This would be a great help.
Kevin Stich
I guess it depends on how much of that functionality you need of course! 90% of the time for me I only need to be able to addRow() and sometimes moveRow(). I'll admit that if you need to be able to add columns as well as rows it gets more complex.
Petriborg
A: 

try

public AchievementTableModel(Object[][] c, Object[] co) {
        super(c,o);
}
Clint
I know it cleans up the code a bit, but does nothing to remedy the situation.
Kevin Stich
A: 

Sounds like when changing your selection is lost.

Does "getSelectedRow()" return anything "BEFORE" you change the model?

If so, then hold that index, change the model and then set that index again.

Probably you need to use a custom ListSelectionModel for that

OscarRyz
It does return the correct information before the model changes, but I don't want the behavior of something being selected based on what was selected before. When the model is changed, I want the user to have to select a new row. In my testing, selecting a row after updating the model and making a new table will always give a -1 regardless of the data or selection.
Kevin Stich
Ohh I get it!!.. It seems like the listener keeps looking at the old table model instead of the new one. Try looking around that. Inspect around the ListSelectionListener and see what or where is that pointing. Also, don't access super.dataVector directly there should be an accessor for that.
OscarRyz
When you ask for the index do you ask current table or accidentally the old table?
kd304
I only keep one reference, so it has to be the current table.
Kevin Stich
+2  A: 

you shouldnt reinitialize your table with a new JTable after you call replace. the fireTableDataChanged() method will alert your existing table that it should repaint. what is happening is that you are looking at the table that you put into the panel, but you are changing the variable to a different instance of JTable. When you query that new, but not visible table, it will give you -1 for the selected row count. it might be helpful if you edit your post to display what is going on in that area of the code.

2nd edit:

instead of this:

  if(model==null)
    model = new AchievementTableModel(cells, columns);
  else
    model.replace(cells, columns);
  if(table==null) {
    table = new JTable(model);
    table.setFillsViewportHeight(true);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getTableHeader().setReorderingAllowed(false);
    table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
    table.getColumnModel().setColumnSelectionAllowed(false);
    table.getTableHeader().setResizingAllowed(false);
  } else
    table.setModel(model);

  column = table.getColumn(columns[0]);
  column.setPreferredWidth(25);
  column = table.getColumn(columns[1]);
  column.setPreferredWidth(225);
  column = table.getColumn(columns[2]);
  column.setPreferredWidth(40);
  table.doLayout();

  add(new JScrollPane(table), BorderLayout.CENTER);

do this instead:

 if(model==null) {
    model = new AchievementTableModel(cells, columns);
 } else {
    model.setDataVector(cells, columns);
 }
 if(table==null) {
    table = new JTable(model);
    table.setFillsViewportHeight(true);
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getTableHeader().setReorderingAllowed(false);
    table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
    table.getColumnModel().setColumnSelectionAllowed(false);
    table.getTableHeader().setResizingAllowed(false);

    column = table.getColumn(columns[0]);
    column.setPreferredWidth(25);
    column = table.getColumn(columns[1]);
    column.setPreferredWidth(225);
    column = table.getColumn(columns[2]);
    column.setPreferredWidth(40);
    table.doLayout();

    add(new JScrollPane(table), BorderLayout.CENTER);
   } else {
    table.setModel(model);
   }

you dont need to add the table to a new scrollpane and re-add it to the panel on each model change.

akf
If I remove this recreating of the JTable, the table becomes empty when I switch the model. What should I use to make the table reappear?
Kevin Stich
the columns are still there, right?
akf
The columns disappear as well.
Kevin Stich
it sounds as if you are clearing out your table model as well. to get this working, what you could do is to create a new table model and instead of creating a new JTable, you could call setModel() on the table.
akf
I set the JTable to setModel instead of new JTable after a null check and made a new model instead. This did not fix the problem, everything still disappears.
Kevin Stich
are you still calling replace()? are you creating a new AchievementTableModel every time you change the combo?
akf
Making a new model with the new data instead of calling replace.
Kevin Stich
I copied the layout settings instead of moving them and all is well. Thanks for your help!
Kevin Stich
+1  A: 

Maybe try using

super.setDataVector(Vector dataVector, Vector ColumnNames);

javax.​swing.​table.​DefaultTableModel
public void setDataVector(Vector dataVector, Vector columnIdentifiers)

From JavaDoc

Replaces the current dataVector instance variable with the new Vector of rows, dataVector. Each row is represented in dataVector as a Vector of Object values. columnIdentifiers are the names of the new columns. The first name in columnIdentifiers is mapped to column 0 in dataVector. Each row in dataVector is adjusted to match the number of columns in columnIdentifiers either by truncating the Vector if it is too long, or adding null values if it is too short. Note that passing in a null value for dataVector results in unspecified behavior, an possibly an exception. Parameters: dataVector - the new data vector columnIdentifiers - the names of the columns

instanceofTom
I tried changing the replace method to this:public void replace(Object[][] c, Object[] co) { setDataVector(convertToVector(c), convertToVector(co)); fireTableDataChanged(); }and that did not fix the problem.
Kevin Stich
When you call convertToVector, does it convert it to a vector of vectors? or a vector of arrays?
instanceofTom
There is one for each, meant for the data and column names respectively.
Kevin Stich
A: 

Another thing I thought of, when you do table= new JTable(model); you are changing the table that the variable 'table' is referring to, however that may not automatically cause the new table to be rendered.

If you table is contained within a ScrollPane, you may need to call ScrollPane.setViewportView(table);

instanceofTom
I tried adding the line of code you suggested, but this didn't affect the situation.
Kevin Stich
+1  A: 
OscarRyz
I'm going to post my updated code because so much has changed in the process of trying to solve this problem. After that I'll see what changes I can make from your example.
Kevin Stich
Yeap. Clean it up a bit ( removing sensitive information ) and post it. I bet it is a very easy to fix.
OscarRyz
+1 for style: dig the screenshot of your answer in the bg!
akf
The code has been updated in the original post, please take a look. Thanks for your help so far.
Kevin Stich