001package jmri.jmrit.logixng.util.swing;
002
003import java.awt.event.WindowEvent;
004import java.awt.event.WindowFocusListener;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.annotation.CheckForNull;
009import javax.annotation.Nonnull;
010import javax.swing.*;
011import javax.swing.event.ChangeEvent;
012import javax.swing.event.ChangeListener;
013
014import jmri.*;
015import jmri.jmrit.logixng.*;
016import jmri.jmrit.logixng.swing.LogixNG_DataDialog;
017import jmri.jmrit.logixng.swing.SwingConfiguratorInterface;
018import jmri.jmrit.logixng.util.LogixNG_SelectTable;
019import jmri.jmrit.logixng.util.parser.ParserException;
020import jmri.util.swing.BeanSelectPanel;
021import jmri.util.swing.JmriJOptionPane;
022
023/**
024 * Swing class for jmri.jmrit.logixng.util.LogixNG_SelectTable.
025 * @author Daniel Bergqvist (C) 2022
026 */
027public class LogixNG_SelectTableSwing {
028
029    private final JDialog _dialog;
030    private final LogixNG_DataDialog _logixNG_DataDialog;
031    private WindowFocusListener _focusListener;
032
033    private JLabel _tableNameLabel;
034    private JLabel _rowNameLabel;
035    private JLabel _columnNameLabel;
036
037    private JButton _selectTableNameButton;
038    private JButton _selectRowNameButton;
039    private JButton _selectColumnNameButton;
040
041    private NamedBeanAddressing _tableNameAddressing = NamedBeanAddressing.Direct;
042    private BeanSelectPanel<NamedTable> _compareToTableBeanPanel;
043    private JTextField _tableNameReferenceTextField;
044    private JTextField _tableNameLocalVariableTextField;
045    private JTextField _tableNameFormulaTextField;
046
047    private NamedBeanAddressing _tableRowAddressing = NamedBeanAddressing.Direct;
048    private JComboBox<String> _tableRowNameComboBox;
049    private JTextField _tableRowNameTextField;
050    private JTextField _tableRowReferenceTextField;
051    private JTextField _tableRowLocalVariableTextField;
052    private JTextField _tableRowFormulaTextField;
053
054    private NamedBeanAddressing _tableColumnAddressing = NamedBeanAddressing.Direct;
055    private JComboBox<String> _tableColumnNameComboBox;
056    private JTextField _tableColumnNameTextField;
057    private JTextField _tableColumnReferenceTextField;
058    private JTextField _tableColumnLocalVariableTextField;
059    private JTextField _tableColumnFormulaTextField;
060
061    private final List<ChangeListener> _changeListeners = new ArrayList<>();
062
063
064    public LogixNG_SelectTableSwing(
065            @Nonnull JDialog dialog, @Nonnull SwingConfiguratorInterface swi) {
066        _logixNG_DataDialog = new LogixNG_DataDialog(swi);
067        _dialog = dialog;
068    }
069
070    private String getTableNameDescription() {
071        String namedBean;
072        switch (_tableNameAddressing) {
073            case Direct:
074                String tableName;
075                if (_compareToTableBeanPanel.getNamedBean() != null) {
076                    tableName = _compareToTableBeanPanel.getNamedBean().getDisplayName();
077                } else {
078                    tableName = Bundle.getMessage("BeanNotSelected");   // NOI18N
079                }
080                namedBean = Bundle.getMessage("AddressByDirect", tableName);    // NOI18N
081                break;
082
083            case Reference:
084                namedBean = Bundle.getMessage("AddressByReference", _tableNameReferenceTextField.getText());    // NOI18N
085                break;
086
087            case LocalVariable:
088                namedBean = Bundle.getMessage("AddressByLocalVariable", _tableNameLocalVariableTextField.getText());    // NOI18N
089                break;
090
091            case Formula:
092                namedBean = Bundle.getMessage("AddressByFormula", _tableNameFormulaTextField.getText());    // NOI18N
093                break;
094
095            default:
096                throw new IllegalArgumentException("invalid _tableNameAddressing: " + _tableNameAddressing.name()); // NOI18N
097        }
098        return Bundle.getMessage("LogixNG_SelectTableSwing_Table", namedBean);  // NOI18N
099    }
100
101    private String getTableRowDescription() {
102        String row;
103        switch (_tableRowAddressing) {
104            case Direct:
105                String rowName =
106                        _tableNameAddressing == NamedBeanAddressing.Direct
107                        ? _tableRowNameComboBox.getItemAt(_tableRowNameComboBox.getSelectedIndex())
108                        : _tableRowNameTextField.getText();
109                row = Bundle.getMessage("AddressByDirect", rowName);   // NOI18N
110                break;
111
112            case Reference:
113                row = Bundle.getMessage("AddressByReference", _tableRowReferenceTextField.getText());   // NOI18N
114                break;
115
116            case LocalVariable:
117                row = Bundle.getMessage("AddressByLocalVariable", _tableRowLocalVariableTextField.getText());   // NOI18N
118                break;
119
120            case Formula:
121                row = Bundle.getMessage("AddressByFormula", _tableRowFormulaTextField.getText());   // NOI18N
122                break;
123
124            default:
125                throw new IllegalArgumentException("invalid _tableRowAddressing: " + _tableRowAddressing.name());   // NOI18N
126        }
127        return Bundle.getMessage("LogixNG_SelectTableSwing_Row", row);  // NOI18N
128    }
129
130    private String getTableColumnDescription() {
131        String column;
132        switch (_tableColumnAddressing) {
133            case Direct:
134                String columnName =
135                        _tableNameAddressing == NamedBeanAddressing.Direct
136                        ? _tableColumnNameComboBox.getItemAt(_tableColumnNameComboBox.getSelectedIndex())
137                        : _tableColumnNameTextField.getText();
138                column = Bundle.getMessage("AddressByDirect", columnName); // NOI18N
139                break;
140
141            case Reference:
142                column = Bundle.getMessage("AddressByReference", _tableColumnReferenceTextField.getText()); // NOI18N
143                break;
144
145            case LocalVariable:
146                column = Bundle.getMessage("AddressByLocalVariable", _tableColumnLocalVariableTextField.getText()); // NOI18N
147                break;
148
149            case Formula:
150                column = Bundle.getMessage("AddressByFormula", _tableColumnFormulaTextField.getText()); // NOI18N
151                break;
152
153            default:
154                throw new IllegalArgumentException("invalid _tableRowAddressing: " + _tableColumnAddressing.name());   // NOI18N
155        }
156        return Bundle.getMessage("LogixNG_SelectTableSwing_Column", column);    // NOI18N
157    }
158
159    private void selectTableNameFinished() {
160        _tableNameAddressing = _logixNG_DataDialog.getAddressing();
161        _tableNameLabel.setText(getTableNameDescription());
162        fireChange();
163    }
164
165    private void selectTableRowFinished() {
166        _tableRowAddressing = _logixNG_DataDialog.getAddressing();
167        _rowNameLabel.setText(getTableRowDescription());
168        fireChange();
169    }
170
171    private void selectTableColumnFinished() {
172        _tableColumnAddressing = _logixNG_DataDialog.getAddressing();
173        _columnNameLabel.setText(getTableColumnDescription());
174        fireChange();
175    }
176
177    private void setupRowOrColumnNameComboBox(@CheckForNull LogixNG_SelectTable selectTable) {
178        String rowName = selectTable != null ? selectTable.getTableRowName() : null;
179        String columnName = selectTable != null ? selectTable.getTableColumnName() : null;
180
181        _tableRowNameComboBox.removeAllItems();
182        _tableColumnNameComboBox.removeAllItems();
183
184        NamedTable table = _compareToTableBeanPanel.getNamedBean();
185        if (table != null) {
186            for (int row=0; row <= table.numRows(); row++) {
187                // If the header is null or empty, treat the row as a comment
188                Object header = table.getCell(row, 0);
189                if ((header != null) && (!header.toString().isEmpty())) {
190                    _tableRowNameComboBox.addItem(header.toString());
191                }
192            }
193            for (int column=0; column <= table.numColumns(); column++) {
194                // If the header is null or empty, treat the row as a comment
195                Object header = table.getCell(0, column);
196                if ((header != null) && (!header.toString().isEmpty())) {
197                    _tableColumnNameComboBox.addItem(header.toString());
198                }
199            }
200            _tableRowNameComboBox.setSelectedItem(rowName);
201            _tableColumnNameComboBox.setSelectedItem(columnName);
202        }
203    }
204
205
206    public void addChangeListener(ChangeListener l) {
207        _changeListeners.add(l);
208    }
209
210
211    public void removeChangeListener(ChangeListener l) {
212        _changeListeners.remove(l);
213    }
214
215    private void fireChange() {
216        for (var l : _changeListeners) {
217            l.stateChanged(new ChangeEvent(this));
218        }
219    }
220
221    public JPanel createPanel(@CheckForNull LogixNG_SelectTable selectTable) {
222
223        _compareToTableBeanPanel = new BeanSelectPanel<>(InstanceManager.getDefault(NamedTableManager.class), null);
224
225        _tableNameLabel = new JLabel();
226        _rowNameLabel = new JLabel();
227        _columnNameLabel = new JLabel();
228
229        _tableNameAddressing = NamedBeanAddressing.Direct;
230        _compareToTableBeanPanel = new BeanSelectPanel<>(InstanceManager.getDefault(NamedTableManager.class), null);
231        _compareToTableBeanPanel.getBeanCombo().addActionListener((evt) -> {
232            setupRowOrColumnNameComboBox(selectTable);
233        });
234
235        _tableRowNameComboBox = new JComboBox<>();
236        _tableRowNameTextField = new JTextField(30);
237        _tableColumnNameComboBox = new JComboBox<>();
238        _tableColumnNameTextField = new JTextField(30);
239
240        _selectTableNameButton = new JButton(Bundle.getMessage("LogixNG_SelectTableSwing_Select"));     // NOI18N
241        _selectRowNameButton = new JButton(Bundle.getMessage("LogixNG_SelectTableSwing_Select"));       // NOI18N
242        _selectColumnNameButton = new JButton(Bundle.getMessage("LogixNG_SelectTableSwing_Select"));    // NOI18N
243
244        _tableNameReferenceTextField = new JTextField(30);
245        _tableNameLocalVariableTextField = new JTextField(30);
246        _tableNameFormulaTextField = new JTextField(30);
247
248        _tableRowReferenceTextField = new JTextField(30);
249        _tableRowLocalVariableTextField = new JTextField(30);
250        _tableRowFormulaTextField = new JTextField(30);
251
252        _tableColumnReferenceTextField = new JTextField(30);
253        _tableColumnLocalVariableTextField = new JTextField(30);
254        _tableColumnFormulaTextField = new JTextField(30);
255
256        _selectTableNameButton.addActionListener((evt) -> {
257            _logixNG_DataDialog.showDialog(
258                    Bundle.getMessage("LogixNG_SelectTableSwing_SelectTable"),
259                    _tableNameAddressing,
260                    _compareToTableBeanPanel,
261                    _tableNameReferenceTextField,
262                    _tableNameLocalVariableTextField,
263                    _tableNameFormulaTextField,
264                    this::selectTableNameFinished);
265        });
266        _selectRowNameButton.addActionListener((evt) -> {
267            if (_tableNameAddressing == NamedBeanAddressing.Direct) {
268                _logixNG_DataDialog.showDialog(
269                        Bundle.getMessage("LogixNG_SelectTableSwing_SelectRow"),
270                        _tableRowAddressing,
271                        _tableRowNameComboBox,
272                        _tableRowReferenceTextField,
273                        _tableRowLocalVariableTextField,
274                        _tableRowFormulaTextField,
275                        this::selectTableRowFinished);
276            } else {
277                _logixNG_DataDialog.showDialog(
278                        Bundle.getMessage("LogixNG_SelectTableSwing_SelectRow"),
279                        _tableRowAddressing,
280                        _tableRowNameTextField,
281                        _tableRowReferenceTextField,
282                        _tableRowLocalVariableTextField,
283                        _tableRowFormulaTextField,
284                        this::selectTableRowFinished);
285            }
286        });
287        _selectColumnNameButton.addActionListener((evt) -> {
288            if (_tableNameAddressing == NamedBeanAddressing.Direct) {
289                _logixNG_DataDialog.showDialog(
290                        Bundle.getMessage("LogixNG_SelectTableSwing_SelectColumn"),
291                        _tableColumnAddressing,
292                        _tableColumnNameComboBox,
293                        _tableColumnReferenceTextField,
294                        _tableColumnLocalVariableTextField,
295                        _tableColumnFormulaTextField,
296                        this::selectTableColumnFinished);
297            } else {
298                _logixNG_DataDialog.showDialog(
299                        Bundle.getMessage("LogixNG_SelectTableSwing_SelectColumn"),
300                        _tableColumnAddressing,
301                        _tableColumnNameTextField,
302                        _tableColumnReferenceTextField,
303                        _tableColumnLocalVariableTextField,
304                        _tableColumnFormulaTextField,
305                        this::selectTableColumnFinished);
306            }
307        });
308
309        JPanel panel = new JPanel();
310        panel.setLayout(new java.awt.GridBagLayout());
311
312        java.awt.GridBagConstraints constraints = new java.awt.GridBagConstraints();
313        constraints.gridwidth = 1;
314        constraints.gridheight = 1;
315        constraints.gridx = 0;
316        constraints.gridy = 0;
317        constraints.anchor = java.awt.GridBagConstraints.EAST;
318        panel.add(_selectTableNameButton, constraints);
319        constraints.gridy = 1;
320        panel.add(_selectRowNameButton, constraints);
321        constraints.gridy = 2;
322        panel.add(_selectColumnNameButton, constraints);
323        constraints.gridx = 0;
324        constraints.gridy = 0;
325        constraints.anchor = java.awt.GridBagConstraints.WEST;
326        panel.add(_tableNameLabel, constraints);
327        constraints.gridy = 1;
328        panel.add(_rowNameLabel, constraints);
329        constraints.gridy = 2;
330        panel.add(_columnNameLabel, constraints);
331        constraints.gridx = 1;
332        constraints.gridy = 0;
333        constraints.anchor = java.awt.GridBagConstraints.WEST;
334        panel.add(new JLabel("  "), constraints);
335        constraints.gridx = 2;
336        constraints.gridy = 0;
337        constraints.anchor = java.awt.GridBagConstraints.WEST;
338        panel.add(_selectTableNameButton, constraints);
339        constraints.gridy = 1;
340        panel.add(_selectRowNameButton, constraints);
341        constraints.gridy = 2;
342        panel.add(_selectColumnNameButton, constraints);
343
344
345        if (selectTable != null) {
346            _tableNameAddressing = selectTable.getTableNameAddressing();
347
348            switch (_tableNameAddressing) {
349                case Direct:
350                    if (selectTable.getTable() != null) {
351                        _compareToTableBeanPanel.setDefaultNamedBean(selectTable.getTable().getBean());
352                    }
353                    break;
354                case Reference: _tableNameReferenceTextField.setText(selectTable.getTableNameReference()); break;
355                case LocalVariable: _tableNameLocalVariableTextField.setText(selectTable.getTableNameLocalVariable()); break;
356                case Formula: _tableNameFormulaTextField.setText(selectTable.getTableNameFormula()); break;
357                default: throw new IllegalArgumentException("invalid _tableNameAddressing: " + _tableNameAddressing.name());    // NOI18N
358            }
359
360            _tableRowAddressing = selectTable.getTableRowAddressing();
361            switch (_tableRowAddressing) {
362                case Direct:
363                    if (_tableNameAddressing == NamedBeanAddressing.Direct) {
364                        _tableRowNameComboBox.setSelectedItem(selectTable.getTableRowName());
365                    } else {
366                        _tableRowNameTextField.setText(selectTable.getTableRowName());
367                    }
368                    break;
369                case Reference: _tableRowReferenceTextField.setText(selectTable.getTableRowReference()); break;
370                case LocalVariable: _tableRowLocalVariableTextField.setText(selectTable.getTableRowLocalVariable()); break;
371                case Formula: _tableRowFormulaTextField.setText(selectTable.getTableRowFormula()); break;
372                default: throw new IllegalArgumentException("invalid _tableRowAddressing: " + _tableRowAddressing.name());  // NOI18N
373            }
374
375            _tableColumnAddressing = selectTable.getTableColumnAddressing();
376            switch (_tableColumnAddressing) {
377                case Direct:
378                    if (_tableNameAddressing == NamedBeanAddressing.Direct) {
379                        _tableColumnNameComboBox.setSelectedItem(selectTable.getTableColumnName());
380                    } else {
381                        _tableColumnNameTextField.setText(selectTable.getTableColumnName());
382                    }
383                    break;
384                case Reference: _tableColumnReferenceTextField.setText(selectTable.getTableColumnReference()); break;
385                case LocalVariable: _tableColumnLocalVariableTextField.setText(selectTable.getTableColumnLocalVariable()); break;
386                case Formula: _tableColumnFormulaTextField.setText(selectTable.getTableColumnFormula()); break;
387                default: throw new IllegalArgumentException("invalid _tableColumnAddressing: " + _tableColumnAddressing.name());    // NOI18N
388            }
389        }
390
391        _tableNameLabel.setText(getTableNameDescription());
392        _rowNameLabel.setText(getTableRowDescription());
393        _columnNameLabel.setText(getTableColumnDescription());
394
395        _focusListener = new WindowFocusListener(){
396            @Override
397            public void windowGainedFocus(WindowEvent e) {
398                _logixNG_DataDialog.checkOpenDialog();
399            }
400
401            @Override
402            public void windowLostFocus(WindowEvent e) {
403                // Do nothing
404            }
405        };
406        _dialog.addWindowFocusListener(_focusListener);
407
408        return panel;
409    }
410
411    public boolean validate(@Nonnull LogixNG_SelectTable selectTable, @Nonnull List<String> errorMessages) {
412
413        try {
414            switch (_tableNameAddressing) {
415                case Direct:
416                    NamedTable table = _compareToTableBeanPanel.getNamedBean();
417                    if (table != null) selectTable.setTable(table);
418                    else selectTable.removeTable();
419                    break;
420                case Reference: selectTable.setTableNameReference(_tableNameReferenceTextField.getText()); break;
421                case LocalVariable: selectTable.setTableNameLocalVariable(_tableNameLocalVariableTextField.getText()); break;
422                case Formula: selectTable.setTableNameFormula(_tableNameFormulaTextField.getText()); break;
423                default: throw new IllegalArgumentException("invalid _tableNameAddressing: " + _tableNameAddressing.name());
424            }
425
426            String rowName =
427                    _tableNameAddressing == NamedBeanAddressing.Direct
428                    ? _tableRowNameComboBox.getItemAt(_tableRowNameComboBox.getSelectedIndex())
429                    : _tableRowNameTextField.getText();
430            switch (_tableRowAddressing) {
431                case Direct: selectTable.setTableRowName(rowName); break;
432                case Reference: selectTable.setTableRowReference(_tableRowReferenceTextField.getText()); break;
433                case LocalVariable: selectTable.setTableRowLocalVariable(_tableRowLocalVariableTextField.getText()); break;
434                case Formula: selectTable.setTableRowFormula(_tableRowFormulaTextField.getText()); break;
435                default: throw new IllegalArgumentException("invalid _tableRowAddressing: " + _tableRowAddressing.name());
436            }
437
438            String columnName =
439                    _tableNameAddressing == NamedBeanAddressing.Direct
440                    ? _tableColumnNameComboBox.getItemAt(_tableColumnNameComboBox.getSelectedIndex())
441                    : _tableColumnNameTextField.getText();
442            switch (_tableColumnAddressing) {
443                case Direct: selectTable.setTableColumnName(columnName); break;
444                case Reference: selectTable.setTableColumnReference(_tableColumnReferenceTextField.getText()); break;
445                case LocalVariable: selectTable.setTableColumnLocalVariable(_tableColumnLocalVariableTextField.getText()); break;
446                case Formula: selectTable.setTableColumnFormula(_tableColumnFormulaTextField.getText()); break;
447                default: throw new IllegalArgumentException("invalid _tableColumnAddressing: " + _tableColumnAddressing.name());
448            }
449        } catch (IllegalArgumentException e) {
450            errorMessages.add("Invalid value: " + e.getMessage());
451            return false;
452        } catch (ParserException e) {
453            errorMessages.add("Cannot parse formula: " + e.getMessage());
454            return false;
455        }
456        return true;
457    }
458
459    public void updateObject(@Nonnull LogixNG_SelectTable selectTable) {
460
461        try {
462            selectTable.setTableNameAddressing(_tableNameAddressing);
463            switch (_tableNameAddressing) {
464                case Direct:
465                    NamedTable table = _compareToTableBeanPanel.getNamedBean();
466                    if (table != null) selectTable.setTable(table);
467                    else selectTable.removeTable();
468                    break;
469                case Reference: selectTable.setTableNameReference(_tableNameReferenceTextField.getText()); break;
470                case LocalVariable: selectTable.setTableNameLocalVariable(_tableNameLocalVariableTextField.getText()); break;
471                case Formula: selectTable.setTableNameFormula(_tableNameFormulaTextField.getText()); break;
472                default: throw new IllegalArgumentException("invalid _tableNameAddressing: " + _tableNameAddressing.name());
473            }
474
475            selectTable.setTableRowAddressing(_tableRowAddressing);
476            String rowName =
477                    _tableNameAddressing == NamedBeanAddressing.Direct
478                    ? _tableRowNameComboBox.getItemAt(_tableRowNameComboBox.getSelectedIndex())
479                    : _tableRowNameTextField.getText();
480            switch (_tableRowAddressing) {
481                case Direct: selectTable.setTableRowName(rowName); break;
482                case Reference: selectTable.setTableRowReference(_tableRowReferenceTextField.getText()); break;
483                case LocalVariable: selectTable.setTableRowLocalVariable(_tableRowLocalVariableTextField.getText()); break;
484                case Formula: selectTable.setTableRowFormula(_tableRowFormulaTextField.getText()); break;
485                default: throw new IllegalArgumentException("invalid _tableRowAddressing: " + _tableRowAddressing.name());
486            }
487
488            selectTable.setTableColumnAddressing(_tableColumnAddressing);
489            String columnName =
490                    _tableNameAddressing == NamedBeanAddressing.Direct
491                    ? _tableColumnNameComboBox.getItemAt(_tableColumnNameComboBox.getSelectedIndex())
492                    : _tableColumnNameTextField.getText();
493            switch (_tableColumnAddressing) {
494                case Direct: selectTable.setTableColumnName(columnName); break;
495                case Reference: selectTable.setTableColumnReference(_tableColumnReferenceTextField.getText()); break;
496                case LocalVariable: selectTable.setTableColumnLocalVariable(_tableColumnLocalVariableTextField.getText()); break;
497                case Formula: selectTable.setTableColumnFormula(_tableColumnFormulaTextField.getText()); break;
498                default: throw new IllegalArgumentException("invalid _tableColumnAddressing: " + _tableColumnAddressing.name());
499            }
500        } catch (ParserException e) {
501            throw new RuntimeException("ParserException: "+e.getMessage(), e);
502        }
503    }
504
505    /** {@inheritDoc} */
506    @Override
507    public String toString() {
508        return Bundle.getMessage("LocalVariable_Short");
509    }
510
511    public boolean canClose() {
512        if (_logixNG_DataDialog.checkOpenDialog()) {
513            JmriJOptionPane.showMessageDialog(_dialog,
514                    Bundle.getMessage("Error_InSelectMode"), // NOI18N
515                    Bundle.getMessage("ErrorTitle"), // NOI18N
516                    JmriJOptionPane.ERROR_MESSAGE);
517            return false;
518        }
519        return true;
520    }
521
522    public void setEnabled(boolean enabled) {
523        _selectTableNameButton.setEnabled(enabled);
524        _selectRowNameButton.setEnabled(enabled);
525        _selectColumnNameButton.setEnabled(enabled);
526    }
527
528    public void dispose() {
529        _logixNG_DataDialog.dispose();
530        _dialog.removeWindowFocusListener(_focusListener);
531    }
532
533}