We need a CellEditor
for a JTable
for editing a large multiline-text.
We tried using a popup visually extending the TableCell
, which was overlapping the cells to the right and bottom. This led to various problems if the cell was in the right bottom corner, near the screen-boundaries, etc.
Then we decided to use a modal JDialog
for editing the cell-value. So the users could move the dialog around, and we could persist its size and position.
Now the problems started ;-)
We are not able to "forward" the first typed character to the Dialog.
There are many examples on stack overflow, where this problem is solved for custom CellEditor
s which are displayed directly in the Table(Cell), for example here: Losing first character in JTable panel based cell editor
The following SSCCE (from camickrs answer: https://mcmap.net/q/541821/-jtable-design-to-synchronize-with-back-end-data-structure) shows that the first keystroke in the second TableColumn gets lost most of the time.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
/**
* Example taken from this answer: https://mcmap.net/q/541821/-jtable-design-to-synchronize-with-back-end-data-structure
*
* @author camickr
*/
public class TablePopupEditor extends DefaultCellEditor
{
private PopupDialog popup;
private String currentText = "";
private JButton editorComponent1;
public TablePopupEditor()
{
super( new JTextField() );
setClickCountToStart( 2 );
// Use a JButton as the editor component
editorComponent1 = new JButton();
editorComponent1.setBackground( Color.white );
editorComponent1.setBorderPainted( false );
editorComponent1.setContentAreaFilled( false );
// Set up the dialog where we do the actual editing
popup = new PopupDialog();
}
@Override
public Object getCellEditorValue()
{
return currentText;
}
@Override
public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column )
{
SwingUtilities.invokeLater( new Runnable()
{
@Override
public void run()
{
System.out.println( "run" );
popup.setText( currentText );
Point p = editorComponent1.getLocationOnScreen();
popup.setLocation( p.x, p.y + editorComponent1.getSize().height );
popup.setVisible( true );
fireEditingStopped();
}
} );
currentText = value.toString();
editorComponent1.setText( currentText );
return editorComponent1;
}
/*
* Simple dialog containing the actual editing component
*/
class PopupDialog extends JDialog implements ActionListener
{
private JTextArea textArea;
public PopupDialog()
{
super( (Frame) null, "Change Description", true );
textArea = new JTextArea( 5, 20 );
textArea.setLineWrap( true );
textArea.setWrapStyleWord( true );
KeyStroke keyStroke = KeyStroke.getKeyStroke( "ENTER" );
textArea.getInputMap().put( keyStroke, "none" );
JScrollPane scrollPane = new JScrollPane( textArea );
getContentPane().add( scrollPane );
JButton cancel = new JButton( "Cancel" );
cancel.addActionListener( this );
JButton ok = new JButton( "Ok" );
ok.setPreferredSize( cancel.getPreferredSize() );
ok.addActionListener( this );
JPanel buttons = new JPanel();
buttons.add( ok );
buttons.add( cancel );
getContentPane().add( buttons, BorderLayout.SOUTH );
pack();
getRootPane().setDefaultButton( ok );
}
public void setText( String text )
{
textArea.setText( text );
}
/*
* Save the changed text before hiding the popup
*/
@Override
public void actionPerformed( ActionEvent e )
{
if ( "Ok".equals( e.getActionCommand() ) )
{
currentText = textArea.getText();
}
textArea.requestFocusInWindow();
setVisible( false );
}
}
public static void main( String[] args )
{
String[] columnNames = { "Item", "Description" };
Object[][] data =
{ { "Item 1", "Description of Item 1" }, { "Item 2", "Description of Item 2" }, { "Item 3", "Description of Item 3" } };
JTable table = new JTable( data, columnNames );
table.getColumnModel().getColumn( 1 ).setPreferredWidth( 300 );
table.setPreferredScrollableViewportSize( table.getPreferredSize() );
JScrollPane scrollPane = new JScrollPane( table );
// Use the popup editor on the second column
TablePopupEditor popupEditor = new TablePopupEditor();
table.getColumnModel().getColumn( 1 ).setCellEditor( popupEditor );
JFrame frame = new JFrame( "Popup Editor Test" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().add( scrollPane );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
Is there a reliable way to catch the first character?