I have a very simple problem here.
When the user clicks on an "Edit" button after he has selected a row in my JTable
, the software checks if this row is allowed to be edited.
If it is, I would like to put the focus in the first cell of that row WITH a blinking cursor so that the user can directly start typing in the cell.
I can successfully set if the row is editable thanks to the isEditable() method and I use table.editCellAt(selectedRow, 0)
to start editing.
However
1) No blinking cursor is appearing in that cell
2) The user can not type in the cell right away (he still has to double click the cell)
Any suggestion on how to achieve this?
////////////////////UPDATE//////////////////////
Although MadProgrammer's comment solves the problem, it only solves part of it but that's because I have not been precise enough.
Indeed, when I perform the steps he describes with a "classic JTable", that is:
table.editCellAt(selectedRow, 0);
table.setSurrendersFocusOnKeystroke(true);
table.getEditorComponent().requestFocus();
I get the blinking cursor AND the ability to type in right away.
HOWEVER the part which was missing is that I give 2 options to the user.
1) He can either directly select a row in the table by hand and for this the solution is fine.
2) But I also offer the user the ability to use a JTextField to find the row he is searching more easily. For this I use a TableRowSorter with a regexFilter to filter the displayed rows. And when I try MadProgrammer's solution there (with filtering on) I get a java.lang.NullPointerException and no further editing is happening.
Here is the exception:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at dialogs.DialogEditCouleurs.actionPerformed(DialogEditCouleurs.java:229)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.WaitDispatchSupport$2.run(Unknown Source)
at java.awt.WaitDispatchSupport$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.awt.WaitDispatchSupport.enter(Unknown Source)
at java.awt.Dialog.show(Unknown Source)
at java.awt.Component.show(Unknown Source)
at java.awt.Component.setVisible(Unknown Source)
at java.awt.Window.setVisible(Unknown Source)
at java.awt.Dialog.setVisible(Unknown Source)
at dialogs.DialogEditCouleurs.<init>(DialogEditCouleurs.java:123)
at panels.PanelPropertiesEdit.actionPerformed(PanelPropertiesEdit.java:940)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Here is the line causing the exception :
table.getEditorComponent().requestFocus();
Here is the actual behaviour of the software:
1) No filter : row is selected, but Edit JButton not yet clicked.
2) No filter : JButton is clicked --> expected correct behaviour.
Now the problematic behaviour :
1) Filtering on : row is selected, but Edit JButton not yet clicked.
2) Filtering on : Edit JButton clicked --> no change and above Exception raised (don't bother the tooltip)
Here is the relevant part of the JDIalog:
public class DialogEditColors extends JDialog implements ActionListener, KeyListener
{
final WebNotificationPopup notificationPopup = new WebNotificationPopup();
final TableRowSorter<TableModel> sorter;
private JTable tableau = null;
private EditTableModel model = null;
private JPanel panelBoutons = null;
private WebTextField txtFieldSearch = null;
private JLabel lblTitle = null;
private JButton btnAdd = null, btnDelete = null, btnEdit = null;
private JButton btnAnnuler = null, btnSaveCouleur = null;
private JScrollPane scroller = null;
private ColorDao colorDao = new ColorDao(Share.connection);
public DialogEditColors()
{
super();
setSize(439, 313);
setTitle(" Edition couleurs");
getContentPane().setLayout(null);
setModal(true);
setResizable(false);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setLocationRelativeTo(null);
btnSaveCouleur = new JButton("Enregistrer");
btnSaveCouleur.setBounds(317, 247, 89, 26);
btnSaveCouleur.addActionListener(this);
btnAnnuler = new JButton("Annuler");
btnAnnuler.setBounds(214, 247, 89, 26);
btnAnnuler.addActionListener(this);
btnAdd = new JButton("");
btnAdd.setBounds(new Rectangle(10, 8, 33, 26));
btnAdd.addActionListener(this);
btnDelete = new JButton("");
btnDelete.setBounds(new Rectangle(53, 8, 33, 26));
btnDelete.addActionListener(this);
btnEdit = new JButton("");
btnEdit.setBounds(new Rectangle(96, 8, 33, 26));
btnEdit.addActionListener(this);
panelBoutons = new JPanel();
panelBoutons.setBorder(new LineBorder(Color.GRAY));
panelBoutons.setBounds(27, 11, 378, 43);
panelBoutons.setLayout(null);
txtFieldSearch = new WebTextField("", 10);
txtFieldSearch.setBounds(193, 9, 175, 24);
panelBoutons.add(txtFieldSearch);
txtFieldSearch.setTrailingComponent(new WebImage(IconUtil.createIcon("/images/search.png").getImage()));
txtFieldSearch.addKeyListener(this);
Object[][] data = new Object[colorDao.findAll().size()][2];
String[] title = { "Couleur", "Description" };
int i = 0;
for (Couleur coul : colorsDao.findAll())
{
data[i][0] = coul.getNom();
data[i][1] = coul.getDescription();
i++;
}
model = new EditTableModel(data, title);
tableau = new JTable(model)
{//COLORING THE BACKGROUND IN ALTERNATE COLORS
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component returnComp = super.prepareRenderer(renderer, row, column);
Color alternateColor = new Color(242, 242, 242);
Color whiteColor = Color.WHITE;
if ( !returnComp.getBackground().equals(getSelectionBackground()) )
{
Color bg = (row % 2 == 0 ? alternateColor : whiteColor);
returnComp.setBackground(bg);
bg = null;
}
return returnComp;
};
};
tableau.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tableau.setCellSelectionEnabled(true);
sorter = new TableRowSorter<TableModel>(model);
tableau.setRowSorter(sorter);
sorter.addRowSorterListener(tableau);
scroller = new JScrollPane(tableau);
scroller.setBounds(27, 65, 378, 171);
addComponents();
setVisible(true);
}//END OF CONSTRUCTOR
private void addComponents()
{
getContentPane().add(scroller);
getContentPane().add(btnSaveCouleur);
getContentPane().add(btnAnnuler);
panelBoutons.add(btnAdd);
panelBoutons.add(btnDelete);
panelBoutons.add(btnEdit);
getContentPane().add(panelBoutons);
}//END OF METHOD
public void actionPerformed(ActionEvent e)
{
if ( e.getSource() == btnEdit )
{
int selectedRow = 0;
if ( tableau.getSelectedRowCount() != 0 ) //THERE IS A CHOSEN COLOR IN JTABLE
{
String selectedColor = (tableau.getValueAt(tableau.getSelectedRow(), tableau.getSelectedColumn()))
.toString();
//WE'RE TESTING IF THE COLOR IS READONLY
if ( colorDao.findByName(selectedColor).get(0).getReadOnly() == true )
{//IF THE COLOR IS READONLY
notificationPopup.setIcon(NotificationIcon.error);
notificationPopup.setContent("This item is readonly : impossible to edit it !");
NotificationManager.showNotification(notificationPopup);
}
else
{////THE COLOR IS NOT READONLY --> EDITING IS ALLOWED
//1) NOTIFY THE MODEL THAT EDITING IS ALLOWED
model.setEditingValidated(true);
//2) TEST WETHER FILTER IS ACTIVE OR NOT TO SEE IF INDEX CONVERSION IS NEEDED
if ( txtFieldSearch.getText() == "" )
{//NO FILTER
selectedRow = tableau.getSelectedRow();
}
else
{//FILTER IS ACTIVE
int modelIndex = tableau.convertRowIndexToModel(tableau.getSelectedRow());
selectedRow = modelIndex;
}
model.setEditingValidatedRowNb(selectedRow);
tableau.editCellAt(selectedRow, 0);
tableau.setSurrendersFocusOnKeystroke(true);
tableau.getEditorComponent().requestFocus();
}
}
else
{ //NO CHOSEN COLOR
notificationPopup.setIcon(NotificationIcon.error);
notificationPopup.setContent("No chosen color !");
NotificationManager.showNotification(notificationPopup);
}
}
else if ( e.getSource() == btnAnnuler )
{//WE LEAVE THE DIALOG WITHOUT DOING ANYTHING
Share.chosenColor = null;
this.dispose();
}
}//END OF METHOD
public void keyReleased(KeyEvent e)
{
if ( e.getSource() == txtFieldSearch )
{
String text = txtFieldSearch.getText();
if ( text.length() == 0 )
{
sorter.setRowFilter(null);
}
else
{
sorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
}
((AbstractTableModel) tableau.getModel()).fireTableDataChanged();
}
}//END OF METHOD
}//END OF CLASS
And here is the relevant part of the TableModel :
public class EditTableModel extends AbstractTableModel implements Serializable
{
protected Vector dataVector;
protected Vector columnIdentifiers;
protected boolean isEditingValidated = false;
protected int editingValidatedRowNb = 0;
public boolean isCellEditable(int row, int column)
{
//IF EDITING IS NOT VALIDATED NOTHING IS EDITABLE / RETURN FALSE
if ( !isEditingValidated )
{
return false;
}
//ELSE A FURTHER TEST IS NEEDED TO DECIDE
else
{
//IF THE CURRENT ROW IS THE ROW FOR WHICH EDITING IS VALIDATED RETURN TRUE
if ( row == editingValidatedRowNb )
{
return true;
}
//ELSE THE ROW IS ANOTHER ROW SO RETURN FALSE
else
{
return false;
}
}
}//END OF METHOD
}//END OF CLASS
Suggestions anyone?
SurrendersFocusOnKeystroke
property of theJTable
and make sure that the editor actually has focus – Uralian