001package jmri.jmrit.logixng.expressions.swing;
002
003import java.awt.Color;
004import java.util.List;
005
006import javax.annotation.CheckForNull;
007import javax.annotation.Nonnull;
008import javax.swing.*;
009import javax.swing.event.ChangeEvent;
010
011import jmri.InstanceManager;
012import jmri.NamedBeanHandle;
013import jmri.NamedBeanHandleManager;
014import jmri.SignalHead;
015import jmri.SignalHeadManager;
016import jmri.jmrit.logixng.*;
017import jmri.jmrit.logixng.expressions.ExpressionSignalHead;
018import jmri.jmrit.logixng.swing.SwingConfiguratorInterface;
019import jmri.jmrit.logixng.util.swing.LogixNG_SelectNamedBeanSwing;
020import jmri.jmrit.logixng.util.parser.ParserException;
021import jmri.util.swing.BeanSelectPanel;
022import jmri.util.swing.JComboBoxUtil;
023
024/**
025 * Configures an ExpressionSignalHead object with a Swing JPanel.
026 *
027 * @author Daniel Bergqvist Copyright 2021
028 */
029public class ExpressionSignalHeadSwing extends AbstractDigitalExpressionSwing {
030
031    public static final int NUM_COLUMNS_TEXT_FIELDS = 20;
032
033    private LogixNG_SelectNamedBeanSwing<SignalHead> _selectNamedBeanSwing;
034
035    private JTabbedPane _tabbedPaneQueryType;
036    private JPanel _panelQueryTypeDirect;
037    private JPanel _panelQueryTypeReference;
038    private JPanel _panelQueryTypeLocalVariable;
039    private JPanel _panelQueryTypeFormula;
040
041    private JComboBox<ExpressionSignalHead.QueryType> _operationComboBox;
042    private JTextField _signalHeadQueryReferenceTextField;
043    private JTextField _signalHeadQueryLocalVariableTextField;
044    private JTextField _signalHeadQueryFormulaTextField;
045
046    private JTabbedPane _tabbedPaneAppearanceType;
047    private JPanel _panelAppearanceTypeDirect;
048    private JPanel _panelAppearanceTypeReference;
049    private JPanel _panelAppearanceTypeLocalVariable;
050    private JPanel _panelAppearanceTypeFormula;
051
052    private JComboBox<SignalHeadAppearance> _signalHeadAppearanceComboBox;
053    private JTextField _signalHeadAppearanceReferenceTextField;
054    private JTextField _signalHeadAppearanceLocalVariableTextField;
055    private JTextField _signalHeadAppearanceFormulaTextField;
056
057    private BeanSelectPanel<SignalHead> _exampleSignalHeadBeanPanel;
058
059
060    public ExpressionSignalHeadSwing() {
061    }
062
063    public ExpressionSignalHeadSwing(JDialog dialog) {
064        super.setJDialog(dialog);
065    }
066
067    @Override
068    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
069        ExpressionSignalHead expression = (ExpressionSignalHead)object;
070
071        _selectNamedBeanSwing = new LogixNG_SelectNamedBeanSwing<>(
072                InstanceManager.getDefault(SignalHeadManager.class), getJDialog(), this);
073
074        panel = new JPanel();
075        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
076
077        JPanel _tabbedPaneNamedBean;
078
079        if (expression != null) {
080            _tabbedPaneNamedBean = _selectNamedBeanSwing.createPanel(expression.getSelectNamedBean());
081        } else {
082            _tabbedPaneNamedBean = _selectNamedBeanSwing.createPanel(null);
083        }
084
085        JPanel examplePanel = new JPanel();
086        JPanel innerExamplePanel = new JPanel();
087        innerExamplePanel.setBorder(BorderFactory.createLineBorder(Color.black));
088        _exampleSignalHeadBeanPanel = new BeanSelectPanel<>(InstanceManager.getDefault(SignalHeadManager.class), null);
089        innerExamplePanel.add(_exampleSignalHeadBeanPanel);
090
091        _exampleSignalHeadBeanPanel.getBeanCombo().addActionListener((java.awt.event.ActionEvent e) -> {
092            setAppearanceComboBox(null);
093        });
094
095
096        JPanel expressionPanel = new JPanel();
097
098        _selectNamedBeanSwing.addAddressingListener((ChangeEvent e) -> {
099            setGuiEnabledStates();
100        });
101
102        _selectNamedBeanSwing.getBeanSelectPanel().getBeanCombo()
103                .addActionListener((java.awt.event.ActionEvent e) -> {
104            setAppearanceComboBox(null);
105        });
106
107        // Set up the tabbed pane for selecting the operation
108        _tabbedPaneQueryType = new JTabbedPane();
109        _panelQueryTypeDirect = new javax.swing.JPanel();
110        _panelQueryTypeDirect.setLayout(new BoxLayout(_panelQueryTypeDirect, BoxLayout.Y_AXIS));
111        _panelQueryTypeReference = new javax.swing.JPanel();
112        _panelQueryTypeReference.setLayout(new BoxLayout(_panelQueryTypeReference, BoxLayout.Y_AXIS));
113        _panelQueryTypeLocalVariable = new javax.swing.JPanel();
114        _panelQueryTypeLocalVariable.setLayout(new BoxLayout(_panelQueryTypeLocalVariable, BoxLayout.Y_AXIS));
115        _panelQueryTypeFormula = new javax.swing.JPanel();
116        _panelQueryTypeFormula.setLayout(new BoxLayout(_panelQueryTypeFormula, BoxLayout.Y_AXIS));
117
118        _tabbedPaneQueryType.addTab(NamedBeanAddressing.Direct.toString(), _panelQueryTypeDirect);
119        _tabbedPaneQueryType.addTab(NamedBeanAddressing.Reference.toString(), _panelQueryTypeReference);
120        _tabbedPaneQueryType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelQueryTypeLocalVariable);
121        _tabbedPaneQueryType.addTab(NamedBeanAddressing.Formula.toString(), _panelQueryTypeFormula);
122
123        _tabbedPaneQueryType.addChangeListener((ChangeEvent e) -> {
124            setGuiEnabledStates();
125        });
126
127        _operationComboBox = new JComboBox<>();
128        for (ExpressionSignalHead.QueryType e : ExpressionSignalHead.QueryType.values()) {
129            _operationComboBox.addItem(e);
130        }
131        JComboBoxUtil.setupComboBoxMaxRows(_operationComboBox);
132
133        _operationComboBox.addActionListener(e -> {
134            setGuiEnabledStates();
135        });
136
137        _panelQueryTypeDirect.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Query")));
138        _panelQueryTypeDirect.add(_operationComboBox);
139
140        _signalHeadQueryReferenceTextField = new JTextField();
141        _signalHeadQueryReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
142        _panelQueryTypeReference.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Query")));
143        _panelQueryTypeReference.add(_signalHeadQueryReferenceTextField);
144
145        _signalHeadQueryLocalVariableTextField = new JTextField();
146        _signalHeadQueryLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
147        _panelQueryTypeLocalVariable.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Query")));
148        _panelQueryTypeLocalVariable.add(_signalHeadQueryLocalVariableTextField);
149
150        _signalHeadQueryFormulaTextField = new JTextField();
151        _signalHeadQueryFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
152        _panelQueryTypeFormula.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Query")));
153        _panelQueryTypeFormula.add(_signalHeadQueryFormulaTextField);
154
155
156        // Set up the tabbed pane for selecting the appearance
157        _tabbedPaneAppearanceType = new JTabbedPane();
158        _panelAppearanceTypeDirect = new javax.swing.JPanel();
159        _panelAppearanceTypeDirect.setLayout(new BoxLayout(_panelAppearanceTypeDirect, BoxLayout.Y_AXIS));
160        _panelAppearanceTypeReference = new javax.swing.JPanel();
161        _panelAppearanceTypeReference.setLayout(new BoxLayout(_panelAppearanceTypeReference, BoxLayout.Y_AXIS));
162        _panelAppearanceTypeLocalVariable = new javax.swing.JPanel();
163        _panelAppearanceTypeLocalVariable.setLayout(new BoxLayout(_panelAppearanceTypeLocalVariable, BoxLayout.Y_AXIS));
164        _panelAppearanceTypeFormula = new javax.swing.JPanel();
165        _panelAppearanceTypeFormula.setLayout(new BoxLayout(_panelAppearanceTypeFormula, BoxLayout.Y_AXIS));
166
167        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Direct.toString(), _panelAppearanceTypeDirect);
168        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Reference.toString(), _panelAppearanceTypeReference);
169        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelAppearanceTypeLocalVariable);
170        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Formula.toString(), _panelAppearanceTypeFormula);
171
172        _tabbedPaneAppearanceType.addChangeListener((ChangeEvent e) -> {
173            setGuiEnabledStates();
174        });
175
176        _signalHeadAppearanceComboBox = new JComboBox<>();
177        _panelAppearanceTypeDirect.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Appearance")));
178        _panelAppearanceTypeDirect.add(_signalHeadAppearanceComboBox);
179
180        _signalHeadAppearanceReferenceTextField = new JTextField();
181        _signalHeadAppearanceReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
182        _panelAppearanceTypeReference.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Appearance")));
183        _panelAppearanceTypeReference.add(_signalHeadAppearanceReferenceTextField);
184
185        _signalHeadAppearanceLocalVariableTextField = new JTextField();
186        _signalHeadAppearanceLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
187        _panelAppearanceTypeLocalVariable.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Appearance")));
188        _panelAppearanceTypeLocalVariable.add(_signalHeadAppearanceLocalVariableTextField);
189
190        _signalHeadAppearanceFormulaTextField = new JTextField();
191        _signalHeadAppearanceFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
192        _panelAppearanceTypeFormula.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_Appearance")));
193        _panelAppearanceTypeFormula.add(_signalHeadAppearanceFormulaTextField);
194
195
196        JPanel notePanel = new JPanel();
197        notePanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.white));
198        examplePanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.white));
199
200        JLabel noteLabel = new JLabel(Bundle.getMessage("SignalExampleText",
201                Bundle.getMessage("SignalExampleHead"),
202                Bundle.getMessage("SignalExampleAppearances")));
203        notePanel.add(noteLabel);
204
205
206        examplePanel.add(new JLabel(Bundle.getMessage("ExpressionSignalHead_ExampleBean")));
207        examplePanel.add(innerExamplePanel);
208
209
210        if (expression != null) {
211            if (expression.getSelectExampleNamedBean().getNamedBean() != null) {
212                _exampleSignalHeadBeanPanel.setDefaultNamedBean(expression.getSelectExampleNamedBean().getNamedBean().getBean());
213            }
214
215            switch (expression.getQueryAddressing()) {
216                case Direct: _tabbedPaneQueryType.setSelectedComponent(_panelQueryTypeDirect); break;
217                case Reference: _tabbedPaneQueryType.setSelectedComponent(_panelQueryTypeReference); break;
218                case LocalVariable: _tabbedPaneQueryType.setSelectedComponent(_panelQueryTypeLocalVariable); break;
219                case Formula: _tabbedPaneQueryType.setSelectedComponent(_panelQueryTypeFormula); break;
220                default: throw new IllegalArgumentException("invalid _addressing state: " + expression.getQueryAddressing().name());
221            }
222            _operationComboBox.setSelectedItem(expression.getQueryType());
223            _signalHeadQueryReferenceTextField.setText(expression.getQueryReference());
224            _signalHeadQueryLocalVariableTextField.setText(expression.getQueryLocalVariable());
225            _signalHeadQueryFormulaTextField.setText(expression.getQueryFormula());
226
227
228            switch (expression.getAppearanceAddressing()) {
229                case Direct: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeDirect); break;
230                case Reference: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeReference); break;
231                case LocalVariable: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeLocalVariable); break;
232                case Formula: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeFormula); break;
233                default: throw new IllegalArgumentException("invalid _addressing state: " + expression.getAppearanceAddressing().name());
234            }
235            _signalHeadAppearanceReferenceTextField.setText(expression.getAppearanceReference());
236            _signalHeadAppearanceLocalVariableTextField.setText(expression.getAppearanceLocalVariable());
237            _signalHeadAppearanceFormulaTextField.setText(expression.getAppearanceFormula());
238
239            jmri.util.ThreadingUtil.runOnGUIEventually(() -> { setAppearanceComboBox(expression); });
240        }
241
242        JComponent[] components = new JComponent[]{
243            _tabbedPaneNamedBean,
244            _tabbedPaneQueryType,
245            _tabbedPaneAppearanceType
246        };
247
248        List<JComponent> componentList = SwingConfiguratorInterface.parseMessage(
249                Bundle.getMessage("ExpressionSignalHead_Components"), components);
250
251        for (JComponent c : componentList) expressionPanel.add(c);
252
253        panel.add(expressionPanel);
254        panel.add(notePanel);
255        panel.add(examplePanel);
256
257        setGuiEnabledStates();
258    }
259
260
261    private void setGuiEnabledStates() {
262        _tabbedPaneAppearanceType.setEnabled(false);
263        _signalHeadAppearanceComboBox.setEnabled(false);
264        _signalHeadAppearanceReferenceTextField.setEnabled(false);
265        _signalHeadAppearanceLocalVariableTextField.setEnabled(false);
266        _signalHeadAppearanceFormulaTextField.setEnabled(false);
267        _exampleSignalHeadBeanPanel.getBeanCombo().setEnabled(false);
268
269        if (_tabbedPaneQueryType.getSelectedComponent() == _panelQueryTypeDirect &&
270                _operationComboBox.getSelectedItem() != ExpressionSignalHead.QueryType.Appearance &&
271                _operationComboBox.getSelectedItem() != ExpressionSignalHead.QueryType.NotAppearance) {
272            return;
273        }
274
275        _tabbedPaneAppearanceType.setEnabled(true);
276
277        if (_selectNamedBeanSwing.getAddressing() != NamedBeanAddressing.Direct &&
278                _tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) {
279            _exampleSignalHeadBeanPanel.getBeanCombo().setEnabled(true);
280        }
281
282        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) {
283            _signalHeadAppearanceComboBox.setEnabled(true);
284        }
285        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeReference) {
286            _signalHeadAppearanceReferenceTextField.setEnabled(true);
287        }
288        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeLocalVariable) {
289            _signalHeadAppearanceLocalVariableTextField.setEnabled(true);
290        }
291        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeFormula) {
292            _signalHeadAppearanceFormulaTextField.setEnabled(true);
293        }
294    }
295
296    private void setAppearanceComboBox(ExpressionSignalHead expression) {
297        SignalHead sh;
298        if (_selectNamedBeanSwing.getAddressing() == NamedBeanAddressing.Direct) {
299            sh = _selectNamedBeanSwing.getBean();
300        } else {
301            sh = _exampleSignalHeadBeanPanel.getBeanCombo().getSelectedItem();
302        }
303
304        if (sh != null) {
305            _signalHeadAppearanceComboBox.removeAllItems();
306            int[] states = sh.getValidStates();
307            for (int s : states) {
308                SignalHeadAppearance sha = new SignalHeadAppearance();
309                sha._state = s;
310                sha._name = sh.getAppearanceName(s);
311                _signalHeadAppearanceComboBox.addItem(sha);
312                if (expression != null) {
313                    if (expression.getAppearance() == s) _signalHeadAppearanceComboBox.setSelectedItem(sha);
314                }
315            }
316            JComboBoxUtil.setupComboBoxMaxRows(_signalHeadAppearanceComboBox);
317        }
318    }
319
320
321    /** {@inheritDoc} */
322    @Override
323    public boolean validate(@Nonnull List<String> errorMessages) {
324        // Create a temporary expression to test formula
325        ExpressionSignalHead expression = new ExpressionSignalHead("IQDE1", null);
326
327        _selectNamedBeanSwing.validate(expression.getSelectNamedBean(), errorMessages);
328
329        try {
330            if (_tabbedPaneQueryType.getSelectedComponent() == _panelQueryTypeReference) {
331                expression.setQueryReference(_signalHeadQueryReferenceTextField.getText());
332            }
333        } catch (IllegalArgumentException e) {
334            errorMessages.add(e.getMessage());
335            return false;
336        }
337
338        return errorMessages.isEmpty();
339    }
340
341    /** {@inheritDoc} */
342    @Override
343    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
344        ExpressionSignalHead expression = new ExpressionSignalHead(systemName, userName);
345        updateObject(expression);
346        return InstanceManager.getDefault(DigitalExpressionManager.class).registerExpression(expression);
347    }
348
349    /** {@inheritDoc} */
350    @Override
351    public void updateObject(@Nonnull Base object) {
352        if (! (object instanceof ExpressionSignalHead)) {
353            throw new IllegalArgumentException("object must be an ExpressionSignalHead but is a: "+object.getClass().getName());
354        }
355        ExpressionSignalHead expression = (ExpressionSignalHead)object;
356
357        _selectNamedBeanSwing.updateObject(expression.getSelectNamedBean());
358
359        if (!_exampleSignalHeadBeanPanel.isEmpty()
360                && (_selectNamedBeanSwing.getAddressing() != NamedBeanAddressing.Direct)
361                && (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect)) {
362
363            SignalHead signalHead = _exampleSignalHeadBeanPanel.getNamedBean();
364            if (signalHead != null) {
365                NamedBeanHandle<SignalHead> handle
366                        = InstanceManager.getDefault(NamedBeanHandleManager.class)
367                                .getNamedBeanHandle(signalHead.getDisplayName(), signalHead);
368                expression.getSelectExampleNamedBean().setNamedBean(handle);
369            }
370        } else {
371            expression.getSelectExampleNamedBean().removeNamedBean();
372        }
373
374        try {
375            if (_tabbedPaneQueryType.getSelectedComponent() == _panelQueryTypeDirect) {
376                expression.setQueryAddressing(NamedBeanAddressing.Direct);
377                expression.setQueryType((ExpressionSignalHead.QueryType)_operationComboBox.getSelectedItem());
378            } else if (_tabbedPaneQueryType.getSelectedComponent() == _panelQueryTypeReference) {
379                expression.setQueryAddressing(NamedBeanAddressing.Reference);
380                expression.setQueryReference(_signalHeadQueryReferenceTextField.getText());
381            } else if (_tabbedPaneQueryType.getSelectedComponent() == _panelQueryTypeLocalVariable) {
382                expression.setQueryAddressing(NamedBeanAddressing.LocalVariable);
383                expression.setQueryLocalVariable(_signalHeadQueryLocalVariableTextField.getText());
384            } else if (_tabbedPaneQueryType.getSelectedComponent() == _panelQueryTypeFormula) {
385                expression.setQueryAddressing(NamedBeanAddressing.Formula);
386                expression.setQueryFormula(_signalHeadQueryFormulaTextField.getText());
387            } else {
388                throw new IllegalArgumentException("_tabbedPaneQueryType has unknown selection");
389            }
390
391            if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) {
392                expression.setAppearanceAddressing(NamedBeanAddressing.Direct);
393
394                if (_signalHeadAppearanceComboBox.getItemCount() > 0) {
395                    expression.setAppearance(_signalHeadAppearanceComboBox
396                            .getItemAt(_signalHeadAppearanceComboBox.getSelectedIndex())._state);
397                }
398            } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeReference) {
399                expression.setAppearanceAddressing(NamedBeanAddressing.Reference);
400                expression.setAppearanceReference(_signalHeadAppearanceReferenceTextField.getText());
401            } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeLocalVariable) {
402                expression.setAppearanceAddressing(NamedBeanAddressing.LocalVariable);
403                expression.setAppearanceLocalVariable(_signalHeadAppearanceLocalVariableTextField.getText());
404            } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeFormula) {
405                expression.setAppearanceAddressing(NamedBeanAddressing.Formula);
406                expression.setAppearanceFormula(_signalHeadAppearanceFormulaTextField.getText());
407            } else {
408                throw new IllegalArgumentException("_tabbedPaneAppearanceType has unknown selection");
409            }
410        } catch (ParserException e) {
411            throw new RuntimeException("ParserException: "+e.getMessage(), e);
412        }
413    }
414
415    /** {@inheritDoc} */
416    @Override
417    public String toString() {
418        return Bundle.getMessage("SignalHead_Short");
419    }
420
421    @Override
422    public void dispose() {
423    }
424
425
426    private static class SignalHeadAppearance {
427
428        private int _state;
429        private String _name;
430
431        @Override
432        public String toString() {
433            return _name;
434        }
435
436    }
437
438//    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionSignalHeadSwing.class);
439
440}