TableModel setCellEditable e configurando automaticamente o valor de volta para false

Atualmente, estou tentando implementar um JPopupMenu na minha JTable que permite o desbloqueio de uma célula para edição usando

@Override 
public void actionPerformed(ActionEvent e){
    if(e.getActionCommand() == "Unlock"){
        pTableModel.setCellEditable(this.getSelectedRow(), this.getSelectedColumn(), true);
    }
}

Este é o método relacionado no TableModel

public void setCellEditable(int row, int col, boolean value) {
    editableCells[row][col] = value;
    this.fireTableCellUpdated(row, col); // I don't think I actually need this 
                                         //because nothing in the cell has changed yet?
}

isCellEditable() então retorna o valor da matriz editableCells [] []. mas onde no meu código devo alterar a célula novamente para não editável quando o foco é perdido?

Em uma nota um pouco relacionada, eu também gostaria que a célula ganhasse foco imediatamente. Eu li sobregetEditorComponent().requestFocus() - mas isso não parece certo porque, como nada está sendo editado naquele momento, a célula é selecionada apenas (e o uso desse método gera uma exceção de nulopointer que parece apoiar o meu processo de pensamento).

Alguém poderia indicar-me a direção correta, por favor? Não vejo onde estou errado. obrigado

** Edit: Para bloquear a célula quando o foco é perdido, tentei adicionar um renderizador de célula personalizado com um focusListener:

private class CustomCellRenderer extends DefaultTableCellRenderer{
    public CustomCellRenderer(){
        addFocusListener(new FocusAdapter(){
            @Override
            public void focusLost(FocusEvent e) {
                pTableModel.setCellEditable(getSelectedRow(), getSelectedColumn(), false);

            }
        });
    }
}

mas isso também não pareceu funcionar, mas provavelmente estou implementando incorretamente (embora tenha verificado que o renderizador foi adicionado)

SSCCE

import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;

public class CellTest {    
    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run(){
                createAndShowGUI();
            }
        });
    }
    private static void createAndShowGUI(){
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        MyPanel panel = new MyPanel();

        f.add(panel);
        f.pack();
        f.setVisible(true);
    }
}


class MyPanel extends JPanel implements ActionListener{
    private JTable table;
    private MyTableModel model;
    private JScrollPane sp;
    private List<Person> people;

    MyPanel(){
        people = new ArrayList<>();
        people.add(new Person("Will", 1));
        model = new MyTableModel(people);

        table = new JTable(model);
        table.addMouseListener(new MouseAdapter(){
            @Override
            public void mousePressed(MouseEvent e){
                maybeShowPopup(e);
            }
            @Override
            public void mouseReleased(MouseEvent e){
                maybeShowPopup(e);
            }
        });

        table.setFillsViewportHeight(true);
        sp = new JScrollPane(table);  
        this.add(sp);
    }

    @Override
    public void actionPerformed(ActionEvent e){
        if(e.getActionCommand().equals("Unlock")) {
                model.setCellEditable(table.getSelectedRow(),table.getSelectedColumn(),true);   
        }
    }

    private JPopupMenu createPopupMenu(){
        JPopupMenu pop = new JPopupMenu();
        JMenuItem item = new JMenuItem("Unlock");
        item.addActionListener(this);
        pop.add(item);
        return pop;
    }

    private void maybeShowPopup(MouseEvent e){
        int currentRow = table.rowAtPoint(e.getPoint());
        if(currentRow >= 0 && currentRow < table.getRowCount())
            table.setRowSelectionInterval(currentRow, currentRow);
        else
            table.clearSelection();

        if(table.getSelectedRow() < 0) return;
        if(e.isPopupTrigger() && e.getComponent() instanceof JTable){
            JPopupMenu pop = createPopupMenu();
            pop.show(e.getComponent(), e.getX(), e.getY());
        }
    }
}

E a classe TableModel e data:

class MyTableModel extends AbstractTableModel{
    private List<Person> data;  
    private String[] colNames = new String[]{"Name","Age"};
    private boolean[][] editableCells;

    public MyTableModel(List<Person> data){
        this.data = data;
        this.editableCells = new boolean[this.getRowCount()][this.getColumnCount()];
    }

    @Override
    public int getRowCount() { return data.size(); }

    @Override
    public int getColumnCount() { return colNames.length; }

    @Override
    public Object getValueAt(int row, int col){
        Person p = data.get(row);
        switch(col){
            case 0 : return p.getName();
            case 1 : return p.getAge();
        }
        return null;
    }

    @Override
    public boolean isCellEditable(int row, int col){
        return editableCells[row][col];
    }

    public void setCellEditable(int row, int col, boolean value){
        editableCells[row][col] = value;
    }

    @Override
    public void setValueAt(Object value, int row, int col){
        Person p = data.get(row);
        switch(col){
            case 0 : p.setName((String)value);break;
            case 1 : p.setAge((int)value);break;
        }
        this.setCellEditable(row, col, false);
        this.fireTableCellUpdated(row, col);
    }

    public void setData(List<Person> data){
        this.data = data;
        this.fireTableDataChanged();
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    public void setName(String name){ this.name = name;}
    public String getName(){ return name; }
    public void setAge(int age){ this.age = age;}
    public int getAge(){ return age; }
}

Desculpas pelo tamanho - eu não sabia como reduzi-lo sem recriar mal o que queria alcançar. Há um erro neste código: ao clicar com o botão direito do mouse e em 'Desbloquear', é necessário que você já tenha clicado com o botão esquerdo do mouse em uma linha, caso contrário eles serão uma exceção arrayIndexOutOfBounds. Ainda não consegui encontrar a causa exata.

Mas, basicamente, quando você clica com o botão direito do mouse e seleciona desbloquear, gostaria que o cursor aparecesse na célula. No momento em que você clica em desbloquear, é necessário clicar duas vezes na célula.

questionAnswers(1)

yourAnswerToTheQuestion