JTextField doesn't update on keypress - java

I have a JTable that uses a custom TableModel. I extended the AbstractCellEditor class and the cell correctly displays the text typed in to the textfield when I double-click the textfield. but when I just single-click select the cell in the table and start typing, the textfield receives the text but when I press enter, it doesn't update the text field. I attached a focus listener to the textfield to troubleshoot and found that it only gains and loses focus when I double click on the field. With a single-click it doesn't gain focus (even though it allows me to edit it). This boggles my mind! I've tried textField.grabFocus(), textField.requestFocusInWindow(), and all sorts of other things. Any suggestions? Thanks!
public class IndexerCellEditor extends AbstractCellEditor implements
TableCellEditor {
private JTextField textField;
private RecordValue currentValue;
public IndexerCellEditor(){
textField = new JTextField();
}
#Override
public boolean isCellEditable(EventObject e){
if(e instanceof MouseEvent){
return ((MouseEvent)e).getClickCount() >= 2;
}
return true;
}
#Override
public Object getCellEditorValue() {
return currentValue;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
textField.setBorder(BorderFactory.createLineBorder(Color.black, 1));
currentValue = (RecordValue) value;
textField.setText(currentValue.getValue());
textField.addFocusListener(new FocusListener(){
#Override
public void focusGained(FocusEvent e) {
System.out.println("focus gained");
}
#Override
public void focusLost(FocusEvent e) {
System.out.println("focus lost");
}
});
textField.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
currentValue.setValue(((JTextField)e.getSource()).getText());
fireEditingStopped();
}
});
return textField;
}
}

OK so after about 8 more hours of banging my head against the wall, I found out 2 things:
I don't need an action listener on the jtextfield because the JTable takes care of that for me. When I hit enter after double clicking + typing OR single-click + typing, JTable automatically calls stopCellEditing(), which brings me to
I need to override stopCellEditing() in my IndexerCellEditor class to save the JTextField text before passing it up to the parent. The code I was missing:
#Override
public boolean stopCellEditing(){
currentValue = textField.getText();
return super.stopCellEditing();
}
Hope this helps anyone with the same problem.
EDIT
This works in my case because I also extended DefaultTableModel, which takes care of notifying the listeners with the method:
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
cells[rowIndex][columnIndex] = (String) aValue;
fireTableCellUpdated(rowIndex, columnIndex);
}
I tested this some more by building two different tables with the same extended DefaultTableModel. Placing them side-by-side in a JPanel, I could edit one cell in one table and upon pressing enter, it would update both the edited cell and its counterpart cell in the other table. In short, the listeners DO need to be notified with a fire... method call somewhere in the project.

Related

How to make JTable with JComboBox respond only on double click instead of single click

So this is the first time I tried using CellEditors for my JTable to embed JComboBox and JSpinner. Everything works fine as expected wherein I can see the values in JComboBox model as well as JSpinner's model values.
However, I noticed that it always displays the JComboBox's values as soon as I make a single click on JTable's column that has the JComboBox.
It's not very user friendly because I think the user would prefer to double click on a JTable's column to get the dropdown box values and select values from it instead of a single click.
How can I change the JComboBox's behaviour to only display itself on double click?
I thought I'd apply a MouseListener to the JComboBox but I don't know what to do next.
Here's what I've written so far.
public class ScheduleDayCellEditor extends DefaultCellEditor{
private JComboBox jcmbDays;
private JTable jtblSchedule;
private DefaultComboBoxModel model;
public ScheduleDayCellEditor(){
super(new JComboBox());
model = new DefaultComboBoxModel(new String[]{"Mon","Tue","Wed","Thu","Fri"});
jcmbDays = new JComboBox(model);
jcmbDays.setEditable(false);
jcmbDays.setSelectedIndex(-1);
jcmbDays.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 2){
//? ? ? ?
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
return jcmbDays;
}
#Override
public Object getCellEditorValue() {
return jcmbDays.getSelectedItem(); //To change body of generated methods, choose Tools | Templates.
}
Here's a screenshot for additional description.
I'd appreciate any help.
Thank you.
If you don't need to extend DefaultCellEditor for some other reason, you can simply invoke its setClickCountToStart() method with a count of 2.
DefaultCellEditor editor = new DefaultCellEditor(jcmbDays);
editor.setClickCountToStart(2);
jcmbColumn.setCellEditor(editor);
Simply override isCellEditable by applying further criterion:
#Override
public boolean isCellEditable(EventObject aAnEvent) {
boolean cellEditable = super.isCellEditable(aAnEvent);
if (cellEditable && aAnEvent instanceof MouseEvent) {
cellEditable = ((MouseEvent) aAnEvent).getClickCount() == 2;
}
return cellEditable;
}

JDateChooser inside JTable cell enter key does not always work

I'm using slightly modified JDateChooserCellEditor class which allows me to put jDateChooser inside my jTable cell. Here is the code of class:
public class JDateChooserCellEditor extends AbstractCellEditor implements
TableCellEditor {
private JDateChooser dateChooser = new JDateChooser();
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
Date date = null;
if (value instanceof Date) {
date = (Date) value;
}
dateChooser.setDateFormatString("dd-MM-yyyy");
dateChooser.setDate(date);
return dateChooser;
}
public Object getCellEditorValue() {
dateChooser.setDateFormatString("dd-MM-yyyy");
return dateChooser.getDate();
}
One thing does not work and I cannot find a solution. When I click for the first time on a cell that has jDateChooser inside, select date and hit enter key - nothing happens. The component maintains its focus but never confirms data. But if I after that select different cell the enter key magically works and date is saved to my jTable. After another try it does not work.. Next try - it works. It is so confusing. Thank you all for any help.
Your TableCellEditor is incorrect. Assuming that your Table model stores instances of Date, invoke setDefaultEditor() as shown with DemoTableModel, which is found in the class com.toedter.calendar.demo.DemoTable and illustrated here.
table.setDefaultEditor(Date.class, new JDateChooserCellEditor());
Addendum: Don't set the date format in the cell editor; specify it in the JDateChooser constructor or using setDateFormatString().
Well, I have found solution for my problem. I think its not the best way but it works and I have lost too much time trying to fix this. Adding listener to jDateChooser component and notifying to stop editing on property change as user kleopatra stated seems to solve the problem.
dateChooser.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("date")) {
stopCellEditing();
}
}
});
Thank you all for help.
A variation of your answer worked for me
private AbstractTableModel model;
.....
... in method getTableCellEditorComponent(JTable table, Object value, boolean isSelected, final int row, final int column) .....
try{
model = (AbstractTableModel) table.getModel();
} catch( Exception e){};
dateChooser.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String pname = evt.getPropertyName();
if ( "date".equals( pname)) {
try{
model.fireTableCellUpdated( row, column);
} catch( Exception e){};
}
}
});
Super side effect - if you make your table smaller, with your mouse, and the JDateChooser exits the window table (margins are outside the table) while editing, then everything works perfect - no workaround necessary - It is a Swing bug that the refresh is done only when you have to take care of other windows outside you (probably an auto refresh all is called - that's why this bug was not discovered yet)
******
yourtable.getColumnModel().getColumn(7).setCellEditor(getDateChooserCellEditor());
or
yourtable.setDefaultEditor(java.util.Date.class, getDateChooserCellEditor());
******
public JDateChooserCellEditor getDateChooserCellEditor() {
JDateChooserCellEditor cellEditor = new JDateChooserCellEditor(){
#Override
public Component getTableCellEditorComponent(JTable jtable, Object o, boolean bln, int i, int i1) {
java.awt.event.ActionListener l = getStopCellEditorActionListener(jtable);
Component component = super.getTableCellEditorComponent(jtable, o, bln, i, i1);
JDateChooser dateChooser = (JDateChooser) component;
JTextField dateEditor = (JTextField) dateChooser.getDateEditor().getUiComponent();
try {
dateEditor.removeActionListener(l);
dateEditor.addActionListener(l);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
return component;
}
};
return cellEditor;
}
private java.awt.event.ActionListener getStopCellEditorActionListener(JTable table) {
return new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
table.editingStopped(new ChangeEvent(table.getCellEditor()));
}
};
}

How to select all text in JTable cell when editing but not when typing?

The default behavior of a JTable is to append to the contents when you start typing, and to place the caret at the clicked location when clicking. I want the behavior of both these things to change, so the contents is replaced when I edit a cell, either by typing or by clicking and then typing. When I click a cell and then change the caret position, however, I want the contents to stay so I can change it.
I know how to select all when the cell becomes editing, by replacing the cell editor with one that selects all inside a SwingUtilities.invokeLater (see elsewhere), but that causes the typing behavior to break. When I do this and start typing in a cell, first the typed character is appended to the string, then it is selected (but the selection is invisible!) and when typing another character the contents gets replaced by that.
Is there a way to replace the contents immediately when typing in a highlighted (but not editing) cell, but select all when clicking a cell?
Here is the code I use for the CellEditor:
public class TextFieldCellEditor extends JTextField implements TableCellEditor
{
private CellEditorListener cellEditorListener = null;
private boolean isInteger = false;
private Object oldValue;
// Start editing
#Override
public Component getTableCellEditorComponent(JTable table, Object obj, boolean isSelected, int row, int column)
{
Color color2 = DefaultLookup.getColor(this, ui, "Table.alternateRowColor");
super.setBackground(color2 != null && (row & 1) == 1? color2 : table.getBackground());
super.setForeground(table.getForeground());
super.setBorder(DefaultLookup.getBorder(this, ui, "Table.focusCellHighlightBorder"));
super.setText(obj.toString());
isInteger = obj instanceof Integer;
if (isInteger)
{
super.setHorizontalAlignment(SwingConstants.RIGHT);
oldValue = obj;
}
// SwingUtilities.invokeLater(new Runnable()
// {
// public void run()
// {
// TextFieldCellEditor.this.selectAll();
// }
// });
return this;
}
// Retrieve e dited value
#Override
public Object getCellEditorValue()
{
if (isInteger)
{
// Try to convert to integer. If input is invalid, revert.
try
{
return new Integer(super.getText());
}
catch (NumberFormatException e)
{
return oldValue;
}
}
return super.getText();
}
#Override
public boolean isCellEditable(EventObject e)
{
return true;
}
#Override
public boolean shouldSelectCell(EventObject e)
{
return true;
}
#Override
public boolean stopCellEditing()
{
cellEditorListener.editingStopped(new ChangeEvent(this));
return true;
}
#Override
public void cancelCellEditing()
{
cellEditorListener.editingCanceled(new ChangeEvent(this));
}
#Override
public void addCellEditorListener(CellEditorListener celleditorlistener)
{
cellEditorListener = celleditorlistener;
}
#Override
public void removeCellEditorListener(CellEditorListener celleditorlistener)
{
if (cellEditorListener == cellEditorListener) cellEditorListener = null;
}
}
In your getTableCellEditorComponent() implementation, add the following:
if (isSelected) {
this.selectAll();
}
As an aside, why not extend AbstractCellEditor or DefaultCellEditor(JTextField textField)? See also How to Use Tables: Using Other Editors.
Addendum: See also Table Select All Renderer and Table Select All Editor.
The cleanest solution I could find for this case was to overwrite the JTable's editCellAt and inform the CellEditor of how the edit was triggered:
#Override
public boolean editCellAt(int row, int column, EventObject e) {
cellEditor.setKeyTriggered(e instanceof KeyEvent);
return super.editCellAt(row, column, e);
}
And here is the relevant CellEditor code:
public class MyCellEditor extends DefaultCellEditor {
private boolean keyTriggered;
public MyCellEditor() {
super(new JTextField());
final JTextField textField = (JTextField) getComponent();
textField.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (!keyTriggered) {
textField.selectAll();
}
}
});
}
});
}
public void setKeyTriggered(boolean keyTriggered) {
this.keyTriggered = keyTriggered;
}
#Override
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column) {
final JTextField textField = (JTextField)
super.getTableCellEditorComponent(table, value, isSelected, row, column);
textField.selectAll();
return textField;
}
}

How can I make a JTable cell do different things on single-click than on double-click?

I am using an editable JTable that contains a column named Subject. When the first row is empty and the user clicks on a subject cell to add new task, by default, the user has to click twice to make the cell editable. I want to make it editable on single-click and have it open another form on double-click. I have tried MouseListener but have not been able to solve it. Is there a way to solve this problem? If so, what is it?
My code:
class mouseRenderer extends DefaultTableCellRenderer {
JLabel lblcell = new JLabel();
public Component getTableCellRendererComponent(JTable table, Object obj, boolean isSelected, boolean hasFocus, int row,
int column) {
ttable.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
selrow = ttable.getSelectedRow();
selcol = ttable.getSelectedColumn();
if(e.getClickCount() == 1) {
if(selrow == 0) {
lblcell.setText("");
}
}
}
});
return lblcell;
}
}
For the one-click to edit, you could try the 'setClickCountToStart()' method of the celleditor used in your jtable.
You can try to create a custom CellEditor like this one and set it with setCellEditor()
public class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor {
public boolean isCellEditable(EventObject evt) {
if (evt instanceof MouseEvent) {
int clickCount;
// For single-click activation
clickCount = 1;
// For double-click activation
clickCount = 2;
// For triple-click activation
clickCount = 3;
return ((MouseEvent)evt).getClickCount() >= clickCount;
}
return true;
}
}
The MouseListener is the way to go for capturing double clicks on a row. It should work fine.
As far as one-click to edit, you might want to select rows using a MouseMotionListener and let the JTable take the single-click to edit. Another option might be to use a MouseListener to detect the cell that was clicked, but that is getting a little messy.

JSpinner Update

I creates a dataTable and cellEditor form one column. This column is simple jSpinner. I have the following problem. When I enter some value in the spinner and select the another row, the value in the previous row won't be changed. If I press , it'll done. If I select or button, it will done too. But if I enter value and change selection, it won't be done. Help, please. Here is the CellEditor code.
public class DurationTableCellEditor extends AbstractCellEditor implements TableCellEditor{
final JSpinner spinner = new JSpinner();
// Initializes the spinner.
public DurationTableCellEditor() {
spinner.setModel(new SpinnerNumberModel(1,1,50000,1));
}
// Prepares the spinner component and returns it.
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
spinner.setValue(new Integer(value.toString()).intValue());
spinner.setCursor(null);
return spinner;
}
// Enables the editor only for double-clicks.
#Override
public boolean isCellEditable(EventObject evt) {
if (evt instanceof MouseEvent) {
return ((MouseEvent)evt).getClickCount() >= 1;
}
return true;
}
// Returns the spinners current value.
public Object getCellEditorValue() {
return spinner.getValue();
}
}
It's not clear how you're updating your data model, but one approach would be to implement ChangeListener in your CellEditor, much as this example implements ItemListener. For reference, see How to Use Tables: Using Other Editors. In particular, look at fireEditingStopped(). Finally, you'll need a corresponding TableCellRenderer.
do commitEdit()
// Returns the spinners current value.
public Object getCellEditorValue() {
spinner.commitEdit();
return spinner.getValue();
}

Categories