I have a cell editor that contains a little button and then a textfield that can be used to edit the value inline
I use setSurrendersFocusOnKeystroke(true) and a focus listener in order to allow a user to start editing immediately from the keyboard, but the trouble is the fisrt key pressed seems to get consumed rather being added to the text field, how can I prevent this ?
Full self contained example below
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
public class PanelTableEditorTest extends JFrame {
private JTable table;
public PanelTableEditorTest() {
this.setLayout(new BorderLayout());
table = new JTable(10, 10);
table.getSelectionModel().setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
table.setCellSelectionEnabled(true);
table.setDefaultEditor(Object.class, new SimpleMultiRowCellEditor());
table.setSurrendersFocusOnKeystroke(true);
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F2, 0),
"none");
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ENTER, 0),
"startEditing");
this.add(table.getTableHeader(), BorderLayout.NORTH);
this.add(table, BorderLayout.CENTER);
pack();
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new PanelTableEditorTest();
}
});
}
public class SimpleMultiRowCellEditor extends DefaultCellEditor {
final JPanel panel;
private final JButton rowCount;
public SimpleMultiRowCellEditor() {
super(new JTextField());
this.setClickCountToStart(1);
rowCount = new JButton();
rowCount.setVisible(true);
panel = new JPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(rowCount);
panel.add(editorComponent);
panel.addFocusListener(new PanelFocusListener());
}
public Component getTableCellEditorComponent(
final JTable table,final Object val, final boolean isSelected,
final int row, final int column) {
rowCount.setText("1");
delegate.setValue(val);
editorComponent.requestFocusInWindow();
return panel;
}
class PanelFocusListener implements FocusListener {
public void focusGained(FocusEvent e) {
editorComponent.requestFocusInWindow();
}
public void focusLost(FocusEvent e) {
}
}
}
}
So I have found a solution, thanks to this article http://jroller.com/santhosh/entry/keyboard_handling_in_tablecelleditor , and some useful discussion abou this and how it can be applied to other components at http://forums.java.net/jive/thread.jspa?messageID=482236񵮼
Don't fully understand the solution this whole area seems to be rather a minefield
I've also added this solution Get correct editing behaviour in JTable using java DefaultCellEditor into this so that when you start editing a field using the keyboard the existing value is replaced, but not when you double click o the field.
My one confusion is that I'm not receiving a Key Event as I'd expect but just null so I've had to account for that.
Ive gone back from using setSurrenderKeystrokes(true) because this causes problems with others editors such as the straightforward textfieldeditor
import javax.swing.*;
import javax.swing.text.Caret;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.EventObject;
public class PanelTableEditorTest extends JFrame
{
private JTable table;
public PanelTableEditorTest()
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e)
{
}
this.setLayout(new BorderLayout());
table = new JTable(4, 4);
table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setCellSelectionEnabled(true);
table.setSurrendersFocusOnKeystroke(false);
table.setDefaultEditor(Object.class,new SimpleMultiRowCellEditor());
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(java.awt.event.
KeyEvent.VK_F2, 0), "none");
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(java.awt.event.
KeyEvent.VK_ENTER, 0), "startEditing");
this.add(table.getTableHeader(), BorderLayout.NORTH);
this.add(table, BorderLayout.CENTER);
pack();
setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
new PanelTableEditorTest();
}
});
}
public class SimpleMultiRowCellEditor extends DefaultCellEditor
{
private EventObject event;
final JPanel panel;
private final JButton rowCount;
public SimpleMultiRowCellEditor()
{
super(new JTextField());
this.setClickCountToStart(1);
rowCount = new JButton();
rowCount.setVisible(true);
panel = new TableEditorPanel();
panel.setRequestFocusEnabled(true);
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(rowCount);
panel.add(editorComponent);
}
public boolean isCellEditable(EventObject anEvent)
{
event=anEvent;
return super.isCellEditable(anEvent);
}
public Component getTableCellEditorComponent(final JTable table, final Object val, final boolean isSelected, final int row, final int column)
{
rowCount.setText("1");
delegate.setValue(val);
if(event instanceof KeyEvent || event==null)
{
final Caret caret = ((JTextField)editorComponent).getCaret();
caret.setDot(0);
((JTextField)editorComponent).setText("");
}
return panel;
}
class TableEditorPanel extends JPanel
{
public void addNotify(){
super.addNotify();
editorComponent.requestFocus();
}
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed){
InputMap map = editorComponent.getInputMap(condition);
ActionMap am = editorComponent.getActionMap();
if(map!=null && am!=null && isEnabled()){
Object binding = map.get(ks);
Action action = (binding==null) ? null : am.get(binding);
if(action!=null){
return SwingUtilities.notifyAction(action, ks, e, editorComponent,
e.getModifiers());
}
}
return false;
}
}
}
}
add a
rowCount.setFocusable(false);
in the SimpleMultiRowCellEditor constructor, to prevent the button to retrieve focus, so that the JTextfield is the only how can have the focus on cell edition