001package jmri.jmrit.logixng.util.swing;
002
003import java.awt.event.ActionEvent;
004import java.util.ArrayList;
005import java.util.List;
006
007import javax.annotation.CheckForNull;
008import javax.annotation.Nonnull;
009import javax.swing.*;
010import javax.swing.table.*;
011
012import jmri.jmrit.logixng.*;
013import jmri.jmrit.logixng.util.LogixNG_SelectStringList;
014import jmri.jmrit.logixng.util.parser.ParserException;
015import jmri.util.table.ButtonEditor;
016import jmri.util.table.ButtonRenderer;
017
018/**
019 * Swing class for jmri.jmrit.logixng.util.LogixNG_SelectStringList.
020 *
021 * @author Daniel Bergqvist (C) 2025
022 */
023public class LogixNG_SelectStringListSwing {
024
025    private JTabbedPane _tabbedPane;
026    private JTable _dataTable;
027    private StringListTableModel _dataTableModel;
028    private JPanel _panelDirect;
029    private JPanel _panelLocalVariable;
030    private JPanel _panelFormula;
031    private JTextField _localVariableTextField;
032    private JTextField _formulaTextField;
033    private JButton _addButton;
034
035
036    public JPanel createPanel(@CheckForNull LogixNG_SelectStringList selectStrList) {
037
038        JPanel panel = new JPanel();
039
040        _tabbedPane = new JTabbedPane();
041        _panelDirect = new javax.swing.JPanel();
042        _panelLocalVariable = new javax.swing.JPanel();
043        _panelFormula = new javax.swing.JPanel();
044        if (selectStrList != null) {
045            if (selectStrList.isOnlyDirectAddressingAllowed()) {
046                _tabbedPane.setEnabled(false);
047                _panelLocalVariable.setEnabled(false);
048                _panelFormula.setEnabled(false);
049            }
050        }
051
052        _tabbedPane.addTab(NamedBeanAddressing.Direct.toString(), _panelDirect);
053        _tabbedPane.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelLocalVariable);
054        _tabbedPane.addTab(NamedBeanAddressing.Formula.toString(), _panelFormula);
055
056        _dataTable = new JTable() {
057            //workaround for  https://bugs.openjdk.org/browse/JDK-4127936
058            @Override
059            public boolean getScrollableTracksViewportWidth() {
060                return getRowCount() == 0 ? super.getScrollableTracksViewportWidth()
061                        : getPreferredSize().width < getParent().getWidth();
062            }
063        };
064
065        if (selectStrList != null) {
066            _dataTableModel = new StringListTableModel(selectStrList.getList());
067        } else {
068            _dataTableModel = new StringListTableModel(null);
069        }
070        _dataTable.setModel(_dataTableModel);
071        _dataTable.getColumnModel().getColumn(0).setPreferredWidth(200);
072        _dataTable.getColumnModel().getColumn(1).setPreferredWidth(
073                new JButton(Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableMoveUp")).getPreferredSize().width);
074        _dataTable.getColumnModel().getColumn(2).setPreferredWidth(
075                new JButton(Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableMoveDown")).getPreferredSize().width);
076        _dataTable.getColumnModel().getColumn(3).setPreferredWidth(
077                new JButton(Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableRemove")).getPreferredSize().width);
078        ButtonRenderer buttonRenderer = new ButtonRenderer();
079        _dataTable.setDefaultRenderer(JButton.class, buttonRenderer);
080        TableCellEditor buttonEditor = new ButtonEditor(new JButton());
081        _dataTable.setDefaultEditor(JButton.class, buttonEditor);
082        JScrollPane scrollPane = new JScrollPane(_dataTable);
083        scrollPane.setPreferredSize(new java.awt.Dimension(600,200));
084        JPanel tablePanel = new JPanel();
085        tablePanel.setLayout(new BoxLayout(tablePanel, BoxLayout.Y_AXIS));
086        tablePanel.add(scrollPane);
087        _addButton = new JButton(Bundle.getMessage("ButtonAdd"));
088        _addButton.addActionListener(_dataTableModel::add);
089        tablePanel.add(_addButton);
090        _panelDirect.add(tablePanel);
091
092        _localVariableTextField = new JTextField();
093        _localVariableTextField.setColumns(30);
094        _panelLocalVariable.add(_localVariableTextField);
095
096        _formulaTextField = new JTextField();
097        _formulaTextField.setColumns(30);
098        _panelFormula.add(_formulaTextField);
099
100
101        if (selectStrList != null) {
102            switch (selectStrList.getAddressing()) {
103                case Direct: _tabbedPane.setSelectedComponent(_panelDirect); break;
104                case LocalVariable: _tabbedPane.setSelectedComponent(_panelLocalVariable); break;
105                case Formula: _tabbedPane.setSelectedComponent(_panelFormula); break;
106                default: throw new IllegalArgumentException("invalid _addressing state: " + selectStrList.getAddressing().name());
107            }
108            _localVariableTextField.setText(selectStrList.getLocalVariable());
109            _formulaTextField.setText(selectStrList.getFormula());
110        }
111
112        panel.add(_tabbedPane);
113        return panel;
114    }
115
116    public boolean validate(
117            @Nonnull LogixNG_SelectStringList selectStrList,
118            @Nonnull List<String> errorMessages) {
119
120        try {
121            selectStrList.setFormula(_formulaTextField.getText());
122            if (_tabbedPane.getSelectedComponent() == _panelDirect) {
123                selectStrList.setAddressing(NamedBeanAddressing.Direct);
124            } else if (_tabbedPane.getSelectedComponent() == _panelLocalVariable) {
125                selectStrList.setAddressing(NamedBeanAddressing.LocalVariable);
126                selectStrList.setLocalVariable(_localVariableTextField.getText());
127            } else if (_tabbedPane.getSelectedComponent() == _panelFormula) {
128                selectStrList.setAddressing(NamedBeanAddressing.Formula);
129                selectStrList.setFormula(_formulaTextField.getText());
130            } else {
131                throw new IllegalArgumentException("_tabbedPane has unknown selection");
132            }
133        } catch (ParserException e) {
134            errorMessages.add("Cannot parse formula: " + e.getMessage());
135            return false;
136        }
137
138        return errorMessages.isEmpty();
139    }
140
141    public void setEnabled(boolean value) {
142        _tabbedPane.setEnabled(value);
143        _dataTable.setEnabled(value);
144        _localVariableTextField.setEnabled(value);
145        _formulaTextField.setEnabled(value);
146    }
147
148    public void updateObject(@Nonnull LogixNG_SelectStringList selectStrList) {
149
150        if (_tabbedPane.getSelectedComponent() == _panelDirect) {
151            List<String> list = selectStrList.getList();
152            list.clear();
153            list.addAll(_dataTableModel.getData());
154        }
155
156        try {
157            if (_tabbedPane.getSelectedComponent() == _panelDirect) {
158                selectStrList.setAddressing(NamedBeanAddressing.Direct);
159            } else if (_tabbedPane.getSelectedComponent() == _panelLocalVariable) {
160                selectStrList.setAddressing(NamedBeanAddressing.LocalVariable);
161                selectStrList.setLocalVariable(_localVariableTextField.getText());
162            } else if (_tabbedPane.getSelectedComponent() == _panelFormula) {
163                selectStrList.setAddressing(NamedBeanAddressing.Formula);
164                selectStrList.setFormula(_formulaTextField.getText());
165            } else {
166                throw new IllegalArgumentException("_tabbedPaneEnum has unknown selection");
167            }
168        } catch (ParserException e) {
169            throw new RuntimeException("ParserException: "+e.getMessage(), e);
170        }
171    }
172
173    public void dispose() {
174    }
175
176
177
178
179    private static class StringListTableModel extends AbstractTableModel {
180
181        public static final int COLUMN_DATA = 0;
182        public static final int COLUMN_MOVE_UP = 1;
183        public static final int COLUMN_MOVE_DOWN = 2;
184        public static final int COLUMN_REMOVE = 3;
185
186        private final List<String> _data = new ArrayList<>();
187
188
189        public StringListTableModel(List<String> data) {
190            if (data != null) {
191                this._data.addAll(data);
192            }
193        }
194
195        /** {@inheritDoc} */
196        @Override
197        public int getRowCount() {
198            return _data.size();
199        }
200
201        /** {@inheritDoc} */
202        @Override
203        public int getColumnCount() {
204            return 4;
205        }
206
207        /** {@inheritDoc} */
208        @Override
209        public String getColumnName(int col) {
210            switch (col) {
211                case COLUMN_DATA:
212                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableData");
213                case COLUMN_MOVE_UP:
214                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableMoveUp");
215                case COLUMN_MOVE_DOWN:
216                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableMoveDown");
217                case COLUMN_REMOVE:
218                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableRemove");
219                default:
220                    throw new IllegalArgumentException("Invalid column");
221            }
222        }
223
224        /** {@inheritDoc} */
225        @Override
226        public Class<?> getColumnClass(int col) {
227            switch (col) {
228                case COLUMN_DATA:
229                    return String.class;
230                case COLUMN_MOVE_UP:
231                case COLUMN_MOVE_DOWN:
232                case COLUMN_REMOVE:
233                    return JButton.class;
234                default:
235                    throw new IllegalArgumentException("Invalid column");
236            }
237        }
238
239        /** {@inheritDoc} */
240        @Override
241        public boolean isCellEditable(int row, int col) {
242            return true;
243        }
244
245        /** {@inheritDoc} */
246        @Override
247        public void setValueAt(Object value, int rowIndex, int columnIndex) {
248            switch (columnIndex) {
249                case COLUMN_DATA:
250                    if (value != null) {
251                        _data.set(rowIndex, value.toString());
252                    } else {
253                        _data.set(rowIndex, "");
254                    }
255                    break;
256                case COLUMN_MOVE_UP:
257                    if (rowIndex > 0) {
258                        String s = _data.get(rowIndex-1);
259                        _data.set(rowIndex-1, _data.get(rowIndex));
260                        _data.set(rowIndex, s);
261                        fireTableRowsUpdated(rowIndex-1, rowIndex);
262                    }
263                    break;
264                case COLUMN_MOVE_DOWN:
265                    if (rowIndex < _data.size()-1) {
266                        String s = _data.get(rowIndex+1);
267                        _data.set(rowIndex+1, _data.get(rowIndex));
268                        _data.set(rowIndex, s);
269                        fireTableRowsUpdated(rowIndex, rowIndex+1);
270                    }
271                    break;
272                case COLUMN_REMOVE:
273                    _data.remove(rowIndex);
274                    fireTableRowsDeleted(rowIndex, rowIndex);
275                    break;
276                default:
277                    throw new IllegalArgumentException("Invalid column");
278            }
279        }
280
281        /** {@inheritDoc} */
282        @Override
283        public Object getValueAt(int rowIndex, int columnIndex) {
284            if (rowIndex >= _data.size()) throw new IllegalArgumentException("Invalid row");
285
286            switch (columnIndex) {
287                case COLUMN_DATA:
288                    return _data.get(rowIndex);
289                case COLUMN_MOVE_UP:
290                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableMoveUp");
291                case COLUMN_MOVE_DOWN:
292                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableMoveDown");
293                case COLUMN_REMOVE:
294                    return Bundle.getMessage("LogixNG_SelectStringListSwing_ColumnVariableRemove");
295                default:
296                    throw new IllegalArgumentException("Invalid column");
297            }
298        }
299
300        public void add(ActionEvent evt) {
301            int row = _data.size();
302            _data.add("");
303            fireTableRowsInserted(row, row);
304        }
305
306        public List<String> getData() {
307            return _data;
308        }
309
310    }
311
312
313//    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNG_SelectStringListSwing.class);
314}