Search code examples
javaswingjtablejcomboboxtablecelleditor

How to set a JComboBox only in specific cell in a JTable


I want to add a JComboBox only inside a cell who suppose to have a list of values. Below is my code but it adds the combo box in all the cells in the column. Let me know what's missing in my code to set the combo box only on the selected cell.

     public class PropertiesTableModel extends AbstractTableModel{

 //this method is called to set the value of each cell
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Field field= (Field) fieldList.get(rowIndex);

        if(columnIndex==0){
            String dataType=field.getFieldDef().getDataType();
            return PropertiesPanel.getPpIns().getDataTypeIcon(dataType);
        }

        if(columnIndex==1){
            return field.getFieldDef().getfName();
        }
        else if (columnIndex==2){
             if(field.getFieldDef().getListValue().size()>0){
                 return createValueListCombo(field.getFieldDef().getListValue());
             }

            return field.getDefaultValue();
        }
        else{
            return null;
        }
    }

        public JComboBox createValueListCombo(List<Value> valueList){
        TableColumn valColumn = table.getColumnModel().getColumn(2);
        JComboBox comboBox=new JComboBox();

        for (Value val: valueList) {
            comboBox.addItem(val.getDescription());
        }
        comboBox.setSelectedIndex(0);
        valColumn.setCellEditor(new DefaultCellEditor(comboBox));
        return comboBox;
    }
}

Solution

  • It's really simple and can be done using two ways

    First of all your model should notify editor/table that the current cell has list of values.

    public class PropertiesTableModel extends AbstractTableModel {
         @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            // previous stuff
            if (columnIndex == 2) {
                // return the actually selected value, 
                // not the first value of the list!
                // also the values changed by setValueAt() must be considered.
                return null; // implement it!
            }
        }
    
        public List<Object> getPossibleValues(int row, int column) {
            // this method should return possible values to select.
            // if cell has no possible values and should be editeb 
            // by a text field this methos should return null
            if (column == 2) {
                 Field field= (Field) fieldList.get(rowIndex);
                 if (field.getFieldDef().getListValue().size() > 0) {
                     return field.getFieldDef().getListValue();
                 }
                 return null; // probably something else for non-list values
             }
        }
    
        public void setValueAt(int row, int column) {
            // you need to store the value chosen by the user
        }
    }
    

    1) Override the method public TableCellEditor getCellEditor(int row, int column) in JTable

    public TableCellEditor getCellEditor(int row, int column) {
        PropertiesTableModel model = (PropertiesTableModel) getModel();
        List<Object> values = model.getPossibleValues(row, column);
        if (values != null) {
           return new DefaultCellEditor(new JComboBox(values.toArray()));
        } else {
           return super.getCellEditor(row, column);
        }
    }
    

    2) You can create a custom editor which delegates all calls to one of two (or more) editors depended on the currently edited cell.

    public class CellEditorMulticaster implements TableCellEditor {
        private DefaultTableCellEditor textEditor;
        private DefaultTableCellEditor currentEditor;
    
        public CellEditorMulticaster() {
            firstEditor = new DefaultTableCellEditor(new JTextField());
        }
    
        Component getTableCellEditorComponent(JTable table, Object value,
                                          boolean isSelected,
                                          int row, int column) {
            PropertiesTableModel model = (PropertiesTableModel) table.getModel();
            List<Object> values = model.getPossibleValues(row, column);
            if (values != null) {
                currentEditor = new DefaultCellEditor(new JComboBox(values.toArray()));
            } else {
                currentEditor = textEditor;
            }
            return currentEditor.getTableCellEditorComponent(table, value,
                   isSelected, row, column);
        }
    
        public Object getCellEditorValue() {
            return currentEditor.getCellEditorValue();
        }
    
        public boolean isCellEditable(EventObject anEvent) {
            JTable table = (JTable) anEvent.getSource;
            int row, col;
            if (anEvent instanceof MouseEvent) {
                MouseEvent evt = (MouseEvent) anEvent;
                row = table.rowAtPoint(evt.getPoint());
                col = table.columnAtPoint(evt.getPoint());
            } else {
                row = table.getSelectedRow();
                col = table.getSelectedColumn();
            }
            PropertiesTableModel model = (PropertiesTableModel) table.getModel();
            List<Object> values = model.getPossibleValues(row, column);
            if (values != null) {
                return true;
            } else {
                return textEditor.isCellEditable(anEvent);
            }
        }
    
        public boolean shouldSelectCell(EventObject anEvent) {
            return true;
        }
    
        public boolean stopCellEditing() {
            return currentEditor.stopCellEditing();
        }
    
        public void  cancelCellEditing() {
            currentEditor.cancelCellEditing();
        }
    
        // same pattern for another methods (delegate to currentEditor)
    }