Setting focus AND blinking cursor on specific JTable cell
Asked Answered
M

1

8

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.

enter image description here

2) No filter : JButton is clicked --> expected correct behaviour.

enter image description here

Now the problematic behaviour :

1) Filtering on : row is selected, but Edit JButton not yet clicked. enter image description here

2) Filtering on : Edit JButton clicked --> no change and above Exception raised (don't bother the tooltip)

enter image description here

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?

Mineralize answered 9/7, 2014 at 1:4 Comment(7)
Two things. Try setting the SurrendersFocusOnKeystroke property of the JTable and make sure that the editor actually has focusUralian
Perfect MadProgrammer, this works ! In case someone else needs the specifics, I have been able to achieve this with the following code: table.editCellAt(selectedRow, 0); table.setSurrendersFocusOnKeystroke(true); table.getEditorComponent().requestFocus(); Thank you very much !!Mineralize
You can answer your own question.Bacteriophage
@Mineralize whatever with Focus should be wrapped into invokeLaterMessalina
@Messalina : this JDialog is part of an application composed of about 70 classes, but the main() method launching everything is wrapping all the UI stuff in javax.swing.SwingUtilities.invokeLater(). Is this sufficient or is there a need to repeat this invokeLater() statement in the JDialog?Mineralize
@Mineralize I'm sure that in camickr's blog is/are there one/two maybe three times about focus and TableCellEditor, again focus (asynchronous) should be wrapped into invokeLaterMessalina
@Messalina thanks for the tip. I'll look there too.Mineralize
M
3

For the second part of your problem, I resolved it as follows:

Firstly I used the

table.setCellSelectionEnabled(true);

Just to make sure that selecting a particular cell in a JTable is enabled.

Then, I used

table.changeSelection(int row, int column, false, false);

The two boolean values above are toggle and extend respectively. false, false here will clear the previous selection and ensure the new cell is selected.

the above snippet was used to select that particular cell in the table. I did so, so that when I use the editCellAt() method it sure shot starts editing the cell which has been currently selected.

If for any reason editCellAt() doesn't work properly then you would get null from the getEditorComponent(). Since no component is editing at present viz the problem in your case.

table.getEditorComponent()

Then to start editing the cell I used

table.editCellAt(int row, int column);

This was used to start editing the cell component programmatically.

and finally to get the cursor blink/focus on that particular cell I used

table().getEditorComponent().requestFocus();

Hope this helps. It works like charm for me. Do let me know if it doesn't.

In short, I added following lines of code:

table.setCellSelectionEnabled(true);
table.changeSelection(int row, int column, false, false);
table.editCellAt(int row, int column);
table().getEditorComponent().requestFocus();
Muscat answered 5/3, 2018 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.