column resized and edit cell at not working

Ok, I'm uncertain if this is a bug or not, but it sure feels like one.

I've noticed that if a column resize is done then a following editCellAt is not working. I've written a small class showing this problem. (Explained below.)

import java.awt.BorderLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.BoxLayout;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JTable;

import javax.swing.table.DefaultTableModel;

publicclass TestJTableResize

{

/**

* @param args

*/

publicstaticvoid main(String[] args)

{

// setup a table

DefaultTableModel dm =new DefaultTableModel();

String[] columnNames ={"This is going to be a really long column header","Column B","Column C","Column D","Column E","Column F","Column G","Column H","Column I","Column J"};

Integer[][] data =new Integer[8][10];

for (int row = 0; row < 8; row++)

{

for (int col = 0; col < 10; ++col)

{

data[row][col] =new Integer((row * 100) + col);

}

}

dm.setDataVector(data, columnNames);

final JTable lJTable =new JTable(dm);

// grow

JButton lJButton1 =new JButton("grow");

lJButton1.addActionListener(new ActionListener()

{

publicvoid actionPerformed(ActionEvent arg0)

{

lJTable.getColumnModel().getColumn(1).setPreferredWidth( lJTable.getColumnModel().getColumn(1).getPreferredWidth() + 10);

lJTable.editCellAt(1, 1);

System.out.println(lJTable.isEditing() +"/" + lJTable.getEditingRow() +"/" + lJTable.getEditingColumn());

}

});

// shrink

JButton lJButton2 =new JButton("shrink");

lJButton2.addActionListener(new ActionListener()

{

publicvoid actionPerformed(ActionEvent arg0)

{

lJTable.getColumnModel().getColumn(1).setPreferredWidth( lJTable.getColumnModel().getColumn(1).getPreferredWidth() - 10);

lJTable.editCellAt(1, 1);

System.out.println(lJTable.isEditing() +"/" + lJTable.getEditingRow() +"/" + lJTable.getEditingColumn());

}

});

// edit

JButton lJButton3 =new JButton("edit");

lJButton3.addActionListener(new ActionListener()

{

publicvoid actionPerformed(ActionEvent arg0)

{

lJTable.editCellAt(1, 1);

System.out.println(lJTable.isEditing() +"/" + lJTable.getEditingRow() +"/" + lJTable.getEditingColumn());

}

});

// show it

JFrame lJFrame =new JFrame();

lJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

lJFrame.setLayout(new BorderLayout());

lJFrame.add(lJTable, BorderLayout.CENTER);

JPanel lJPanel =new JPanel();

lJPanel.setLayout(new BoxLayout(lJPanel, BoxLayout.LINE_AXIS));

lJPanel.add(lJButton1);

lJPanel.add(lJButton2);

lJPanel.add(lJButton3);

lJFrame.add(lJPanel, BorderLayout.SOUTH);

lJFrame.setSize(600, 300);

lJFrame.setVisible(true);

}

}

The class shows a table with some dummy data, and three buttons. The first two buttons will make column 1 grow or shrink and then try to edit cell (1,1). The third button wil only try to edit the cell. For all JVM's I've tested (1.4, 1.5 and 1.6) the first two buttons do not start an edit, even though the JTable is absolutely convinced it is editing (see console output). Even when tracking the keyboard focus (code not included), it shows that the focus is in the cell editor.

[6179 byte] By [tbeernota] at [2007-11-26 17:54:59]
# 1

There's a problem with the events order.

You need to run the editCellAt method after the resize column finishes.

lJButton1.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent arg0)

{

lJTable.getColumnModel().getColumn(1).setPreferredWidth( lJTable.getColumnModel().getColumn(1).getPreferredWidth() + 10);

SwingUtilities.invokeLater(new Runnable() {

public void run() {

lJTable.editCellAt(1, 1);

System.out.println(lJTable.isEditing() + "/" + lJTable.getEditingRow() + "/" + lJTable.getEditingColumn());

}

});

}

});

Rodney_McKaya at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 2

But that would mean that if I execute this on the swing thread (which the actual situation is), then it should work? Like this?

public static void main(String[] args)

{

final TestJTableResize lTestJTableResize = new TestJTableResize();

SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

lTestJTableResize.go();

}

});

}

void go()

{

...

tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 3
Hmmmm.The answer would be: no. The resize will create a paint event, but that is executed AFTER the event where editCellAt is called.Still I'm not quite comfortable with this: a repaint is scheduled but before the table is put into edit mode, should the repaint not paint
tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 4

Basically what I'm saying is that if editCellAt is depending on pending paint events, shouldn't editCellAt also be handled as an event by JTable itself?

My thing is that the resize is done by a listener (it's an JTable addon class that listens to changes in the JTable and resizes automatically). I feel it is strange coding to have a editCellAt in a event without any real reason: it's executed on the AWT thread, there is no other thread being split... It just doesn't seem right.

tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 5
The problem is that setting the column width stops the editing of the cell.So you just have to make sure that the edit is done after the stop editing.There's nothing wrong with running a code with invokeLater, that is what it was invented for.
Rodney_McKaya at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 6

I understand, I really do: but I personally always would like to see that there is logic to constructs in code; the invokeLater totally comes out of the blue in my actual code. In the simplified example it is all clear and good, but not in my real life code.

The problem is that the setPreferredWidth method on a column does not play well with the editCellAt method in the table. The one does a GUI change delayed, while the other does it immediately, and thus in effect reverse the code logic. I consider this "not consistent" and I feel the user of the API should not have to compensate for it (if it were my API...).

So either setPreferredWidth is also done immediately when on the ADT or editCellAt is also done delayed (or at least the visualsation of it).

IMHO

tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 7
Hmmm. I put the invokeLater code into my actual application and there it isn't working...
tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 8

This is working, leave either out and it is not anymore.

iHourTable.editCellAt(lIdx, 2);

final int lIdxFinal = lIdx;

SwingUtilities.invokeLater(new Runnable()

{

public void run()

{

iHourTable.editCellAt(lIdxFinal, 2);

}

});

tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...
# 9
Interestingly enough, this also works..iHourTable.editCellAt(lIdx, 2);iHourTable.editCellAt(lIdx, 2);
tbeernota at 2007-7-9 5:08:06 > top of Java-index,Desktop,Core GUI APIs...