001package jmri.jmrit.logixng.util.swing;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.List;
007
008import javax.annotation.CheckForNull;
009import javax.annotation.Nonnull;
010import javax.swing.*;
011import javax.swing.event.ChangeListener;
012
013import jmri.*;
014import jmri.jmrit.logixng.*;
015import jmri.jmrit.logixng.swing.SwingConfiguratorInterface;
016import jmri.jmrit.logixng.util.LogixNG_SelectEnum;
017import jmri.jmrit.logixng.util.parser.ParserException;
018import jmri.util.swing.BeanSelectPanel;
019import jmri.util.swing.JComboBoxUtil;
020
021/**
022 * Swing class for jmri.jmrit.logixng.util.LogixNG_SelectEnum.
023 *
024 * @param <E> the type of enum
025 *
026 * @author Daniel Bergqvist (C) 2022
027 */
028public class LogixNG_SelectEnumSwing<E extends Enum<?>> {
029
030    private final JDialog _dialog;
031    private final LogixNG_SelectTableSwing _selectTableSwing;
032
033    private LogixNG_SelectEnumSwing_EnumDialog<E> _enumDialog;
034    private E[] _enumArray;
035
036    private JTabbedPane _tabbedPane;
037    private JComboBox<E> _enumComboBox;
038    private JPanel _panelDirect;
039    private JPanel _panelReference;
040    private JPanel _panelMemory;
041    private JPanel _panelLocalVariable;
042    private JPanel _panelFormula;
043    private JPanel _panelTable;
044    private JTextField _referenceTextField;
045    private BeanSelectPanel<Memory> _memoryPanel;
046    private JTextField _localVariableTextField;
047    private JTextField _formulaTextField;
048
049
050    public LogixNG_SelectEnumSwing(
051            @Nonnull JDialog dialog,
052            @Nonnull SwingConfiguratorInterface swi) {
053        _dialog = dialog;
054        _selectTableSwing = new LogixNG_SelectTableSwing(_dialog, swi);
055    }
056
057    public JPanel createPanel(
058            @CheckForNull LogixNG_SelectEnum<E> selectEnum, E[] enumArray) {
059        return createPanel(selectEnum, enumArray, null);
060    }
061
062    public JPanel createPanel(
063            @CheckForNull LogixNG_SelectEnum<E> selectEnum, E[] enumArray, E defaultValue) {
064
065        _enumArray = enumArray;
066
067        JPanel panel = new JPanel();
068
069        _tabbedPane = new JTabbedPane();
070        _panelDirect = new javax.swing.JPanel();
071        _panelReference = new javax.swing.JPanel();
072        _panelMemory = new JPanel();
073        _panelLocalVariable = new javax.swing.JPanel();
074        _panelFormula = new javax.swing.JPanel();
075        if (selectEnum != null) {
076            _panelTable = _selectTableSwing.createPanel(selectEnum.getSelectTable());
077        } else {
078            _panelTable = _selectTableSwing.createPanel(null);
079        }
080
081        _memoryPanel = new BeanSelectPanel<>(InstanceManager.getDefault(MemoryManager.class), null);
082
083        _panelMemory.setLayout(new BoxLayout(_panelMemory, BoxLayout.Y_AXIS));
084        _panelMemory.add(_memoryPanel);
085        _panelMemory.add(createEnumDialogButton());
086
087        _tabbedPane.addTab(NamedBeanAddressing.Direct.toString(), _panelDirect);
088        _tabbedPane.addTab(NamedBeanAddressing.Reference.toString(), _panelReference);
089        _tabbedPane.addTab(NamedBeanAddressing.Memory.toString(), _panelMemory);
090        _tabbedPane.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelLocalVariable);
091        _tabbedPane.addTab(NamedBeanAddressing.Formula.toString(), _panelFormula);
092        _tabbedPane.addTab(NamedBeanAddressing.Table.toString(), _panelTable);
093
094        _enumComboBox = new JComboBox<>();
095        for (E e : enumArray) {
096            _enumComboBox.addItem(e);
097        }
098        JComboBoxUtil.setupComboBoxMaxRows(_enumComboBox);
099        _enumComboBox.setRenderer(new ComboBoxRenderer<>(_enumComboBox.getRenderer()));
100        _panelDirect.add(_enumComboBox);
101
102        _referenceTextField = new JTextField();
103        _referenceTextField.setColumns(30);
104        _panelReference.setLayout(new BoxLayout(_panelReference, BoxLayout.Y_AXIS));
105        _panelReference.add(_referenceTextField);
106        _panelReference.add(createEnumDialogButton());
107
108        _localVariableTextField = new JTextField();
109        _localVariableTextField.setColumns(30);
110        _panelLocalVariable.setLayout(new BoxLayout(_panelLocalVariable, BoxLayout.Y_AXIS));
111        _panelLocalVariable.add(_localVariableTextField);
112        _panelLocalVariable.add(createEnumDialogButton());
113
114        _formulaTextField = new JTextField();
115        _formulaTextField.setColumns(30);
116        _panelFormula.setLayout(new BoxLayout(_panelFormula, BoxLayout.Y_AXIS));
117        _panelFormula.add(_formulaTextField);
118        _panelFormula.add(createEnumDialogButton());
119
120
121        if (defaultValue != null) {
122            _enumComboBox.setSelectedItem(defaultValue);
123        }
124
125        if (selectEnum != null) {
126            switch (selectEnum.getAddressing()) {
127                case Direct: _tabbedPane.setSelectedComponent(_panelDirect); break;
128                case Reference: _tabbedPane.setSelectedComponent(_panelReference); break;
129                case Memory: _tabbedPane.setSelectedComponent(_panelMemory); break;
130                case LocalVariable: _tabbedPane.setSelectedComponent(_panelLocalVariable); break;
131                case Formula: _tabbedPane.setSelectedComponent(_panelFormula); break;
132                case Table: _tabbedPane.setSelectedComponent(_panelTable); break;
133                default: throw new IllegalArgumentException("invalid _addressing state: " + selectEnum.getAddressing().name());
134            }
135            if (selectEnum.getEnum() != null) {
136                _enumComboBox.setSelectedItem(selectEnum.getEnum());
137            }
138            _referenceTextField.setText(selectEnum.getReference());
139            _memoryPanel.setDefaultNamedBean(selectEnum.getMemory());
140            _localVariableTextField.setText(selectEnum.getLocalVariable());
141            _formulaTextField.setText(selectEnum.getFormula());
142        }
143
144        panel.add(_tabbedPane);
145        return panel;
146    }
147
148    public JButton createEnumDialogButton() {
149        JButton enumDialogButton = new JButton(Bundle.getMessage("LogixNG_SelectEnumSwing_ButtonEnumDialog"));  // NOI18N
150        enumDialogButton.addActionListener(this::showEnumDialog);
151        return enumDialogButton;
152    }
153
154    public void showEnumDialog(ActionEvent e) {
155        if (_enumDialog != null) {
156            _enumDialog.setVisible(true);
157        } else {
158            Runnable windowIsClosed = () -> { _enumDialog = null; };
159            _enumDialog = new LogixNG_SelectEnumSwing_EnumDialog<>(_enumArray, windowIsClosed);
160        }
161    }
162
163    public void addAddressingListener(ChangeListener listener) {
164        _tabbedPane.addChangeListener(listener);
165    }
166
167    public void addEnumListener(ActionListener listener) {
168        _enumComboBox.addActionListener(listener);
169    }
170
171    public boolean validate(
172            @Nonnull LogixNG_SelectEnum<E> selectEnum,
173            @Nonnull List<String> errorMessages) {
174        try {
175            if (_tabbedPane.getSelectedComponent() == _panelReference) {
176                selectEnum.setReference(_referenceTextField.getText());
177            }
178        } catch (IllegalArgumentException e) {
179            errorMessages.add(e.getMessage());
180            return false;
181        }
182
183        try {
184            selectEnum.setFormula(_formulaTextField.getText());
185            selectEnum.setAddressing(getAddressing());
186        } catch (ParserException e) {
187            errorMessages.add("Cannot parse formula: " + e.getMessage());
188            return false;
189        }
190
191        _selectTableSwing.validate(selectEnum.getSelectTable(), errorMessages);
192
193        return errorMessages.isEmpty();
194    }
195
196    public void updateObject(@Nonnull LogixNG_SelectEnum<E> selectEnum) {
197
198        if (_tabbedPane.getSelectedComponent() == _panelDirect) {
199            selectEnum.setEnum(_enumComboBox.getItemAt(_enumComboBox.getSelectedIndex()));
200        }
201
202        try {
203            if (_tabbedPane.getSelectedComponent() == _panelDirect) {
204                selectEnum.setAddressing(NamedBeanAddressing.Direct);
205            } else if (_tabbedPane.getSelectedComponent() == _panelReference) {
206                selectEnum.setAddressing(NamedBeanAddressing.Reference);
207                selectEnum.setReference(_referenceTextField.getText());
208            } else if (_tabbedPane.getSelectedComponent() == _panelMemory) {
209                selectEnum.setAddressing(NamedBeanAddressing.Memory);
210                selectEnum.setMemory(_memoryPanel.getNamedBean());
211            } else if (_tabbedPane.getSelectedComponent() == _panelLocalVariable) {
212                selectEnum.setAddressing(NamedBeanAddressing.LocalVariable);
213                selectEnum.setLocalVariable(_localVariableTextField.getText());
214            } else if (_tabbedPane.getSelectedComponent() == _panelFormula) {
215                selectEnum.setAddressing(NamedBeanAddressing.Formula);
216                selectEnum.setFormula(_formulaTextField.getText());
217            } else if (_tabbedPane.getSelectedComponent() == _panelTable) {
218                selectEnum.setAddressing(NamedBeanAddressing.Table);
219            } else {
220                throw new IllegalArgumentException("_tabbedPaneEnum has unknown selection");
221            }
222        } catch (ParserException e) {
223            throw new RuntimeException("ParserException: "+e.getMessage(), e);
224        }
225
226        _selectTableSwing.updateObject(selectEnum.getSelectTable());
227    }
228
229    public boolean isEnumSelected(E e) {
230        if (_tabbedPane.getSelectedComponent() == _panelDirect) {
231            return _enumComboBox.getItemAt(_enumComboBox.getSelectedIndex()) == e;
232        } else {
233            return false;
234        }
235    }
236
237    public boolean isEnumSelectedOrIndirectAddressing(E e) {
238        if (_tabbedPane.getSelectedComponent() == _panelDirect) {
239            return _enumComboBox.getItemAt(_enumComboBox.getSelectedIndex()) == e;
240        } else {
241            return true;
242        }
243    }
244
245    public NamedBeanAddressing getAddressing() {
246        if (_tabbedPane.getSelectedComponent() == _panelDirect) {
247            return NamedBeanAddressing.Direct;
248        } else if (_tabbedPane.getSelectedComponent() == _panelReference) {
249            return NamedBeanAddressing.Reference;
250        } else if (_tabbedPane.getSelectedComponent() == _panelMemory) {
251            return NamedBeanAddressing.Memory;
252        } else if (_tabbedPane.getSelectedComponent() == _panelLocalVariable) {
253            return NamedBeanAddressing.LocalVariable;
254        } else if (_tabbedPane.getSelectedComponent() == _panelFormula) {
255            return NamedBeanAddressing.Formula;
256        } else if (_tabbedPane.getSelectedComponent() == _panelTable) {
257            return NamedBeanAddressing.Table;
258        } else {
259            throw new IllegalArgumentException("_tabbedPane has unknown selection");
260        }
261    }
262
263    public E getEnum() {
264        return _enumComboBox.getItemAt(_enumComboBox.getSelectedIndex());
265    }
266
267    public void setEnum(E e) {
268        _enumComboBox.setSelectedItem(e);
269    }
270
271    public void dispose() {
272        _selectTableSwing.dispose();
273        if (_enumDialog != null) {
274            _enumDialog.setVisible(false);
275            _enumDialog.dispose();
276            _enumDialog = null;
277        }
278    }
279
280
281    private static class ComboBoxRenderer<E> extends JLabel implements ListCellRenderer<E> {
282
283        private final JSeparator _separator = new JSeparator(JSeparator.HORIZONTAL);
284        private final ListCellRenderer<E> _old;
285
286        private ComboBoxRenderer(ListCellRenderer<E> old) {
287            this._old = old;
288        }
289
290        @Override
291        public Component getListCellRendererComponent(JList<? extends E> list,
292                E value, int index, boolean isSelected, boolean cellHasFocus) {
293            if (Base.SEPARATOR.equals(value.toString())) {
294                return _separator;
295            } else {
296                return _old.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
297            }
298        }
299    }
300
301}