Why is JXTable losing input where JTable is not?
Asked Answered
S

2

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();
      }
    } );
  }

}
Sorb answered 30/1, 2015 at 12:59 Comment(2)
Have you tried adding a custom focus listener to JXTable ? See the 2nd and 3rd answer on this post. #1653442Lesterlesya
Good idea, but a FocusListener is too unreliable under different OperatingSystems.Sorb
S
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.

Sorb answered 4/2, 2015 at 13:31 Comment(0)
R
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.

Ranaerancagua answered 3/2, 2015 at 17:22 Comment(1)
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.Sorb

© 2022 - 2024 — McMap. All rights reserved.