8

When I'm using a JXTable to render and edit my data, some input into the CellEditors gets lost. If I click on the Resizing-Divider of the JXTable-ColumnHeader or change the width of the JFrame, the CellEditor gets terminated without commiting the value. The values are saved if I use the JTable.

I want to use the JXTable because of its other features, so is there a way to fix the JXTable?

Screenrecording

Example:

package table.columnresize;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

import org.jdesktop.swingx.JXTable;

/**
 * Demo of differing behaviour of JXTable and JTable. JXTable loses input in a TableCell where JTable persists
 * it.
 * <p>
 * <table border=1>
 * <tr>
 * <th></th>
 * <th>JXTable</th>
 * <th>JTable</th>
 * </tr>
 * <tr>
 * <td>Click on TableColumnHeader</td>
 * <td>saved</td>
 * <td>saved</td>
 * </tr>
 * <tr>
 * <td>Resizing with Divider of TableColumnHeader</td>
 * <td>lost</td>
 * <td>saved</td>
 * </tr>
 * <tr>
 * <td>Changing the width of JFrame</td>
 * <td>lost</td>
 * <td>saved</td>
 * </tr>
 * 
 * </table>
 * </p>
 * 
 * @author bobndrew 2015-01-29
 */
public class JXTableAndJTableEditLossDemo
{
  private static class DataModel extends DefaultTableModel
  {
    public DataModel( Object[][] data, Object[] columnNames )
    {
      super( data, columnNames );
    }
  }

  private static void createAndShowUI()
  {
    Object[][] DATA = { { "One", 1 }, { "Two", 2 }, { "Three", 3 }, { "Four", 4 }, { "Five", 5 } };
    String[] COLUMNS = { "A", "B" };
    DataModel dataModel = new DataModel( DATA, COLUMNS );

    JFrame frame1 = new JFrame( "JXTable" );
    JXTable jXTable = new JXTable( dataModel );
    //does not change anything:    jXTable.setTerminateEditOnFocusLost( true );
    System.out.println( jXTable.isTerminateEditOnFocusLost() );
    frame1.add( new JScrollPane( jXTable ) );
    frame1.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame1.pack();
    frame1.setVisible( true );

    JFrame frame2 = new JFrame( "JTable" );
    JTable jTable = new JTable( dataModel );
    //does not change anything:    jTable.putClientProperty( "terminateEditOnFocusLost", Boolean.FALSE );
    System.out.println( jTable.getClientProperty( "terminateEditOnFocusLost" ) );
    frame2.add( new JScrollPane( jTable ) );
    frame2.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame2.pack();
    frame2.setLocation( (int) frame1.getLocation().getX() + frame1.getWidth() + 100, (int) frame1
        .getLocation().getY() );
    frame2.setVisible( true );
  }

  public static void main( String[] args )
  {
    java.awt.EventQueue.invokeLater( new Runnable()
    {
      @Override
      public void run()
      {
        createAndShowUI();
      }
    } );
  }

}
bobndrew
  • 395
  • 10
  • 32
  • Have you tried adding a custom focus listener to JXTable ? See the 2nd and 3rd answer on this post. http://stackoverflow.com/questions/1652942/can-a-jtable-save-data-whenever-a-cell-loses-focus – a_pradhan Feb 03 '15 at 11:56
  • Good idea, but a FocusListener is too unreliable under different OperatingSystems. – bobndrew Feb 03 '15 at 17:35

2 Answers2

3

While debugging the JXTable and the JTable I found the reason for the loss of CellEdits. The difference is in the method columnMarginChanged():

JXTable:

if (isEditing()) {
  removeEditor();
}

JTable:

if (isEditing() && !getCellEditor().stopCellEditing()) {
  getCellEditor().cancelCellEditing();
} 

At first I thought the removeEditor() method is an enhancement of the JTable... But then I found this OpenJDK changeset from September 2010 which fixes the bug "4330950: Lost newly entered data in the cell when resizing column width". It seems that changes from the JDK were not applied to the SwingX sourcecode.

I will accept my own answer because the reason for different behaviour is now clear. To fix this for me and other SwingX users I will head over to the SwingX mailing list and the bug tracker.

bobndrew
  • 395
  • 10
  • 32
1

When you take a look at the frameInit() method of JTable, you can see that it binds to all AWTEvent.WINDOW* events. In JXTable the initActionsAndBindings() method binds to specific actions (such as value changed), and only for the table.

You will need to add your own listener

    jXTable.getColumnModel().addColumnModelListener(new TableColumnModelListener() {
        @Override
        public void columnMarginChanged(ChangeEvent e) {

        }
    });

and then would have to expose some functionality of the table to allow the event to trigger updating the table. Or perhaps you can trigger an TableModelEvent from there.

kol
  • 76
  • 4
  • `frameInit()` is a method in `JFrame.java` and not `JTable.java`. But your idea with a `TableColumnModelListener` and `columnMarginChanged()` is similar to my findings; see my answer. – bobndrew Feb 04 '15 at 11:47