Reputation: 2294
Okay, this is a hard one to explain but I'll try my best.
I have a JTextField and a JComboBox in a JTable, whose getCellEditor method has been overriden as follows:
public TableCellEditor getCellEditor( int row, int column ) {
if ( column == 3 ) {
// m_table is the JTable
if ( m_table.getSelectedRowCount() == 1 ) {
JComboBox choices = new JComboBox();
choices.setEditable( true );
choices.addItem( new String( "item 1" ) );
return new DefaultCellEditor( choices );
}
return super.getCellEditor( row, column );
}
Here are the behavioral differences (NOTE that from this point on, when I say JTextField or JComboBox, I mean the CELL in the JTable containing either component):
When I click once on a JTextField, the cell is highlighted. Double clicking brings up the caret and I can input text. Whereas, with a JComboBox, single clicking brings up the caret to input text, as well as the combo drop down button.
When I tab or use the arrow keys to navigate to a JTextField and then start typing, the characters I type automatically get entered into the cell. Whereas, when I navigate to a JComboBox the same way and then start typing, nothing happens apart from the combo drop down button appearing. None of the characters I type get entered unless I hit F2 first.
So here's my question: What do I need to do have JComboBoxes behave exactly like JTextFields in the two instances described above?
Please do not ask why I'm doing what I'm doing or suggest alternatives (it's the way it is and I need to do it this way) and yes, I've read the API for all components in question....the problem is, it's a swing API.
Upvotes: 1
Views: 676
Reputation: 51525
An alternative for bullet 2 is to move all the passing-on of the keyEvents into a custom JComboBox implementation: in there, implement its processKeyBinding similar to table.processKeyBinding. Something like:
public static class JTableEditorComboBox extends JComboBox {
@Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
int condition, boolean pressed) {
boolean result = super.processKeyBinding(ks, e, condition, pressed);
if (!result)
result = processKeyBindingForTextField(ks, e, condition, pressed);
return result;
}
private boolean processKeyBindingForTextField(KeyStroke ks, KeyEvent e,
int condition, boolean pressed) {
// sanity check: really used as cellEditor
if (!Boolean.TRUE.equals(getClientProperty("JComboBox.isTableCellEditor"))
|| !isEditable()
|| !(getEditor().getEditorComponent() instanceof JTextField)) return false;
JTextField field = (JTextField) getEditor().getEditorComponent();
// basically c&p JComponent.processKeyBinding (it's protected so
// can't call directly from here)
InputMap map = field.getInputMap(WHEN_FOCUSED);
ActionMap am = field.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, field,
e.getModifiers());
}
}
return false;
}
}
Then, use an instance of that in your cellEditor, like:
JTable table = new JTable(new AncientSwingTeam()) {
@Override
public TableCellEditor getCellEditor(int row, int column) {
if (column == 1) {
// m_table is the JTable
JComboBox choices = new JTableEditorComboBox();
choices.setEditable(true);
choices.addItem(new String("item 1"));
DefaultCellEditor editor = new DefaultCellEditor(choices);
editor.setClickCountToStart(2);
return editor;
}
return super.getCellEditor(row, column);
}
};
Upvotes: 2
Reputation: 2294
Further Googling led me to the following article:
http://www.jroller.com/santhosh/entry/keyboard_handling_in_tablecelleditor
Although it doesn't describe the same issue as mine, it definitely shares some common traits.
Using some of the suggestions in that article, I was able to (atleast) resolve the keyboard bit of my problem (the one described in point 2 of my question). I did this by overriding the processKeyBinding
method of JTable
as follows:
protected boolean processKeyBinding( KeyStroke key_stroke, KeyEvent e, int condition, boolean pressed ) {
Object source = e.getSource();
if ( source instanceof JTable ) {
JTable table = (JTable) source;
Component comp = table.getEditorComponent();
if ( comp instanceof JComboBox ) {
JComboBox combo_box = (JComboBox) comp;
// this bit is quite hacky. Since I want comboboxes to behave exactly like textfields,
// simply check to see how a textfield would handle this event.
JTextField tmp_field = new JTextField();
InputMap input_map = tmp_field.getInputMap( condition );
ActionMap action_map = tmp_field.getActionMap();
if( input_map != null && action_map != null && isEnabled() ) {
Object binding = input_map.get( key_stroke );
Action action = ( binding == null ) ? null : action_map.get( binding );
if( action != null ) {
combo_box.requestFocus();
ComboBoxEditor combo_editor = combo_box.getEditor();
JTextField text_field = (JTextField) combo_editor.getEditorComponent();
if ( e.getKeyChar() == ' ' ) { // backspace
String cur_val = text_field.getText();
if ( ! cur_val.equals( "" ) )
text_field.setText( cur_val.substring( 0, cur_val.length() - 1 ) );
}
else
text_field.setText( text_field.getText() + e.getKeyChar() );
return false;
}
}
}
}
return super.processKeyBinding( key_stroke, e, condition, pressed );
}
This method seems like bit of a hack to me but then again it's swing, so it might be reasonable.
The problem of getting JComboBoxes to behave like JTextFields in the scenario described in point 1 of my question (i.e when using a mouse) is still open, if someone want's to have a go at it.
Ash
Upvotes: 2