001package jmri.jmrit.logixng.actions.swing;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.*;
007
008import javax.swing.*;
009import javax.swing.table.AbstractTableModel;
010import javax.swing.table.DefaultTableCellRenderer;
011import javax.swing.table.TableCellEditor;
012
013import jmri.jmrit.logixng.actions.LogData;
014import jmri.jmrit.logixng.util.parser.ParserException;
015import jmri.util.swing.JComboBoxUtil;
016
017/**
018 * Table model for LogData
019 *
020 * @author Daniel Bergqvist Copyright 2021
021 */
022public class LogDataTableModel extends AbstractTableModel {
023
024    // COLUMN_DUMMY is "hidden" but used when the panel is closed to
025    // ensure that the last edited cell is saved.
026    public static final int COLUMN_TYPE = 0;
027    public static final int COLUMN_DATA = 1;
028    public static final int COLUMN_DELETE = 2;
029    public static final int COLUMN_DUMMY = 3;
030
031    private final List<LogData.Data> _dataList = new ArrayList<>();
032
033
034    public LogDataTableModel(Collection<LogData.Data> namedBeanReference) {
035        if (namedBeanReference != null) _dataList.addAll(namedBeanReference);
036    }
037
038    /** {@inheritDoc} */
039    @Override
040    public int getRowCount() {
041        return _dataList.size();
042    }
043
044    /** {@inheritDoc} */
045    @Override
046    public int getColumnCount() {
047        return COLUMN_DUMMY+1;
048    }
049
050    /** {@inheritDoc} */
051    @Override
052    public String getColumnName(int col) {
053        switch (col) {
054            case COLUMN_TYPE:
055                return Bundle.getMessage("LogData_ColumnType");
056            case COLUMN_DATA:
057                return Bundle.getMessage("LogData_ColumnData");
058            case COLUMN_DELETE:
059                return "";  // no label
060            case COLUMN_DUMMY:
061                return "";  // no label
062            default:
063                throw new IllegalArgumentException("Invalid column");
064        }
065    }
066
067    /** {@inheritDoc} */
068    @Override
069    public Class<?> getColumnClass(int col) {
070        switch (col) {
071            case COLUMN_TYPE:
072                return LogData.DataType.class;
073            case COLUMN_DATA:
074                return String.class;
075            case COLUMN_DELETE:
076                return JButton.class;
077            case COLUMN_DUMMY:
078                return String.class;
079            default:
080                throw new IllegalArgumentException("Invalid column");
081        }
082    }
083
084    /** {@inheritDoc} */
085    @Override
086    public boolean isCellEditable(int row, int col) {
087        return true;
088    }
089
090    /** {@inheritDoc} */
091    @Override
092    public void setValueAt(Object value, int rowIndex, int columnIndex) {
093        LogData.Data ref = _dataList.get(rowIndex);
094
095        switch (columnIndex) {
096            case COLUMN_TYPE:
097                if (value != null) {
098                    LogData.DataType oldType = ref.getDataType();
099                    ref.setDataType((LogData.DataType) value);
100                    if (oldType != ref.getDataType()) ref.setData("");
101                } else {
102                    // This happens if the combo box is open when the user clicks on another cell.
103                    log.debug("Cannot set data type to null");
104                }
105                break;
106            case COLUMN_DATA:
107                ref.setData((String) value);
108                break;
109            case COLUMN_DELETE:
110                delete(rowIndex);
111                break;
112            case COLUMN_DUMMY:
113                break;
114            default:
115                throw new IllegalArgumentException("Invalid column");
116        }
117    }
118
119    /** {@inheritDoc} */
120    @Override
121    public Object getValueAt(int rowIndex, int columnIndex) {
122        if (rowIndex >= _dataList.size()) throw new IllegalArgumentException("Invalid row");
123
124        switch (columnIndex) {
125            case COLUMN_TYPE:
126                return _dataList.get(rowIndex).getDataType();
127            case COLUMN_DATA:
128                return _dataList.get(rowIndex).getData();
129            case COLUMN_DELETE:
130                return Bundle.getMessage("ButtonDelete");  // NOI18N
131            case COLUMN_DUMMY:
132                return "";
133            default:
134                throw new IllegalArgumentException("Invalid column");
135        }
136    }
137
138    public void setColumnsForComboBoxes(JTable table) {
139        JComboBox<LogData.DataType> beanTypeComboBox = new JComboBox<>();
140        table.setRowHeight(beanTypeComboBox.getPreferredSize().height);
141        table.getColumnModel().getColumn(COLUMN_TYPE)
142                .setPreferredWidth((beanTypeComboBox.getPreferredSize().width) + 4);
143    }
144
145    public void add() {
146        int row = _dataList.size();
147        try {
148            _dataList.add(new LogData.Data(LogData.DataType.LocalVariable, ""));
149        } catch (ParserException e) {
150            throw new RuntimeException(e);  // This should never happen
151        }
152        fireTableRowsInserted(row, row);
153    }
154
155    private void delete(int row) {
156        _dataList.remove(row);
157        fireTableRowsDeleted(row, row);
158    }
159
160    public List<LogData.Data> getDataList() {
161        return _dataList;
162    }
163
164
165    public static class CellRenderer extends DefaultTableCellRenderer {
166
167        @Override
168        public Component getTableCellRendererComponent(JTable table, Object value,
169                boolean isSelected, boolean hasFocus, int row, int column) {
170
171            if (column == COLUMN_TYPE) {
172                if (value == null) value = LogData.DataType.LocalVariable;
173
174                if (! (value instanceof LogData.DataType)) {
175                    throw new IllegalArgumentException("value is not an LogData.DataType: " + value.getClass().getName());
176                }
177                setText(((LogData.DataType) value).toString());
178            } else {
179                throw new RuntimeException("Unknown column: "+Integer.toString(column));
180            }
181            return this;
182        }
183    }
184
185
186    public static class DataTypeCellEditor extends AbstractCellEditor
187            implements TableCellEditor, ActionListener {
188
189        private LogData.DataType _beanType;
190
191        @Override
192        public Object getCellEditorValue() {
193            return this._beanType;
194        }
195
196        @Override
197        public Component getTableCellEditorComponent(JTable table, Object value,
198                boolean isSelected, int row, int column) {
199
200            if (value == null) value = LogData.DataType.LocalVariable;
201
202            if (! (value instanceof LogData.DataType)) {
203                throw new IllegalArgumentException("value is not an LogData.DataType: " + value.getClass().getName());
204            }
205
206            JComboBox<LogData.DataType> returnValueTypeComboBox = new JComboBox<>();
207
208            for (LogData.DataType type : LogData.DataType.values()) {
209                returnValueTypeComboBox.addItem(type);
210            }
211            JComboBoxUtil.setupComboBoxMaxRows(returnValueTypeComboBox);
212
213            returnValueTypeComboBox.setSelectedItem(value);
214            returnValueTypeComboBox.addActionListener(this);
215
216            return returnValueTypeComboBox;
217        }
218
219        @Override
220        @SuppressWarnings("unchecked")  // Not possible to check that event.getSource() is instanceof JComboBox<LogData.DataType>
221        public void actionPerformed(ActionEvent event) {
222            if (! (event.getSource() instanceof JComboBox)) {
223                throw new IllegalArgumentException("value is not an JComboBox: " + event.getSource().getClass().getName());
224            }
225            JComboBox<LogData.DataType> returnValueTypeComboBox =
226                    (JComboBox<LogData.DataType>) event.getSource();
227            _beanType = returnValueTypeComboBox.getItemAt(returnValueTypeComboBox.getSelectedIndex());
228
229        }
230
231    }
232
233
234    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogDataTableModel.class);
235
236}