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