001package jmri.jmrit.logixng.actions.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.actions.ActionSignalHead;
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 ActionSignalHead object with a Swing JPanel.
026 *
027 * @author Daniel Bergqvist Copyright 2021
028 */
029public class ActionSignalHeadSwing extends AbstractDigitalActionSwing {
030
031    public static final int NUM_COLUMNS_TEXT_FIELDS = 20;
032
033    private LogixNG_SelectNamedBeanSwing<SignalHead> _selectNamedBeanSwing;
034
035    private JTabbedPane _tabbedPaneOperationType;
036    private JPanel _panelOperationTypeDirect;
037    private JPanel _panelOperationTypeReference;
038    private JPanel _panelOperationTypeLocalVariable;
039    private JPanel _panelOperationTypeFormula;
040
041    private JComboBox<ActionSignalHead.OperationType> _operationComboBox;
042    private JTextField _signalHeadOperationReferenceTextField;
043    private JTextField _signalHeadOperationLocalVariableTextField;
044    private JTextField _signalHeadOperationFormulaTextField;
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 ActionSignalHeadSwing() {
061    }
062
063    public ActionSignalHeadSwing(JDialog dialog) {
064        super.setJDialog(dialog);
065    }
066
067    @Override
068    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
069        ActionSignalHead action = (ActionSignalHead)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 (action != null) {
080            _tabbedPaneNamedBean = _selectNamedBeanSwing.createPanel(action.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 actionPanel = 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        _tabbedPaneOperationType = new JTabbedPane();
109        _panelOperationTypeDirect = new javax.swing.JPanel();
110        _panelOperationTypeDirect.setLayout(new BoxLayout(_panelOperationTypeDirect, BoxLayout.Y_AXIS));
111        _panelOperationTypeReference = new javax.swing.JPanel();
112        _panelOperationTypeReference.setLayout(new BoxLayout(_panelOperationTypeReference, BoxLayout.Y_AXIS));
113        _panelOperationTypeLocalVariable = new javax.swing.JPanel();
114        _panelOperationTypeLocalVariable.setLayout(new BoxLayout(_panelOperationTypeLocalVariable, BoxLayout.Y_AXIS));
115        _panelOperationTypeFormula = new javax.swing.JPanel();
116        _panelOperationTypeFormula.setLayout(new BoxLayout(_panelOperationTypeFormula, BoxLayout.Y_AXIS));
117
118        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Direct.toString(), _panelOperationTypeDirect);
119        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Reference.toString(), _panelOperationTypeReference);
120        _tabbedPaneOperationType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelOperationTypeLocalVariable);
121        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Formula.toString(), _panelOperationTypeFormula);
122
123        _tabbedPaneOperationType.addChangeListener((ChangeEvent e) -> {
124            setGuiEnabledStates();
125        });
126
127        _operationComboBox = new JComboBox<>();
128        for (ActionSignalHead.OperationType e : ActionSignalHead.OperationType.values()) {
129            _operationComboBox.addItem(e);
130        }
131        JComboBoxUtil.setupComboBoxMaxRows(_operationComboBox);
132        _operationComboBox.addActionListener(e -> {
133            setGuiEnabledStates();
134        });
135
136        _panelOperationTypeDirect.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation")));
137        _panelOperationTypeDirect.add(_operationComboBox);
138
139        _signalHeadOperationReferenceTextField = new JTextField();
140        _signalHeadOperationReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
141        _panelOperationTypeReference.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation")));
142        _panelOperationTypeReference.add(_signalHeadOperationReferenceTextField);
143
144        _signalHeadOperationLocalVariableTextField = new JTextField();
145        _signalHeadOperationLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
146        _panelOperationTypeLocalVariable.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation")));
147        _panelOperationTypeLocalVariable.add(_signalHeadOperationLocalVariableTextField);
148
149        _signalHeadOperationFormulaTextField = new JTextField();
150        _signalHeadOperationFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
151        _panelOperationTypeFormula.add(new JLabel(Bundle.getMessage("ActionSignalHead_Operation")));
152        _panelOperationTypeFormula.add(_signalHeadOperationFormulaTextField);
153
154
155        // Set up the tabbed pane for selecting the appearance
156        _tabbedPaneAppearanceType = new JTabbedPane();
157        _panelAppearanceTypeDirect = new javax.swing.JPanel();
158        _panelAppearanceTypeDirect.setLayout(new BoxLayout(_panelAppearanceTypeDirect, BoxLayout.Y_AXIS));
159        _panelAppearanceTypeReference = new javax.swing.JPanel();
160        _panelAppearanceTypeReference.setLayout(new BoxLayout(_panelAppearanceTypeReference, BoxLayout.Y_AXIS));
161        _panelAppearanceTypeLocalVariable = new javax.swing.JPanel();
162        _panelAppearanceTypeLocalVariable.setLayout(new BoxLayout(_panelAppearanceTypeLocalVariable, BoxLayout.Y_AXIS));
163        _panelAppearanceTypeFormula = new javax.swing.JPanel();
164        _panelAppearanceTypeFormula.setLayout(new BoxLayout(_panelAppearanceTypeFormula, BoxLayout.Y_AXIS));
165
166        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Direct.toString(), _panelAppearanceTypeDirect);
167        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Reference.toString(), _panelAppearanceTypeReference);
168        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelAppearanceTypeLocalVariable);
169        _tabbedPaneAppearanceType.addTab(NamedBeanAddressing.Formula.toString(), _panelAppearanceTypeFormula);
170
171        _tabbedPaneAppearanceType.addChangeListener((ChangeEvent e) -> {
172            setGuiEnabledStates();
173        });
174
175        _signalHeadAppearanceComboBox = new JComboBox<>();
176        _panelAppearanceTypeDirect.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance")));
177        _panelAppearanceTypeDirect.add(_signalHeadAppearanceComboBox);
178
179        _signalHeadAppearanceReferenceTextField = new JTextField();
180        _signalHeadAppearanceReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
181        _panelAppearanceTypeReference.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance")));
182        _panelAppearanceTypeReference.add(_signalHeadAppearanceReferenceTextField);
183
184        _signalHeadAppearanceLocalVariableTextField = new JTextField();
185        _signalHeadAppearanceLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
186        _panelAppearanceTypeLocalVariable.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance")));
187        _panelAppearanceTypeLocalVariable.add(_signalHeadAppearanceLocalVariableTextField);
188
189        _signalHeadAppearanceFormulaTextField = new JTextField();
190        _signalHeadAppearanceFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
191        _panelAppearanceTypeFormula.add(new JLabel(Bundle.getMessage("ActionSignalHead_Appearance")));
192        _panelAppearanceTypeFormula.add(_signalHeadAppearanceFormulaTextField);
193
194
195        JPanel notePanel = new JPanel();
196        notePanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.white));
197        examplePanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.white));
198
199        JLabel noteLabel = new JLabel(Bundle.getMessage("SignalExampleText",
200                Bundle.getMessage("SignalExampleHead"),
201                Bundle.getMessage("SignalExampleAppearances")));
202        notePanel.add(noteLabel);
203
204
205        examplePanel.add(new JLabel(Bundle.getMessage("ActionSignalHead_ExampleBean")));
206        examplePanel.add(innerExamplePanel);
207
208
209        if (action != null) {
210            if (action.getSelectExampleNamedBean().getNamedBean() != null) {
211                _exampleSignalHeadBeanPanel.setDefaultNamedBean(action.getSelectExampleNamedBean().getNamedBean().getBean());
212            }
213
214            switch (action.getOperationAddressing()) {
215                case Direct: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeDirect); break;
216                case Reference: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeReference); break;
217                case LocalVariable: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeLocalVariable); break;
218                case Formula: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeFormula); break;
219                default: throw new IllegalArgumentException("invalid _addressing state: " + action.getOperationAddressing().name());
220            }
221            _operationComboBox.setSelectedItem(action.getOperationType());
222            _signalHeadOperationReferenceTextField.setText(action.getOperationReference());
223            _signalHeadOperationLocalVariableTextField.setText(action.getOperationLocalVariable());
224            _signalHeadOperationFormulaTextField.setText(action.getOperationFormula());
225
226
227            switch (action.getAppearanceAddressing()) {
228                case Direct: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeDirect); break;
229                case Reference: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeReference); break;
230                case LocalVariable: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeLocalVariable); break;
231                case Formula: _tabbedPaneAppearanceType.setSelectedComponent(_panelAppearanceTypeFormula); break;
232                default: throw new IllegalArgumentException("invalid _addressing state: " + action.getAppearanceAddressing().name());
233            }
234            _signalHeadAppearanceReferenceTextField.setText(action.getAppearanceReference());
235            _signalHeadAppearanceLocalVariableTextField.setText(action.getAppearanceLocalVariable());
236            _signalHeadAppearanceFormulaTextField.setText(action.getAppearanceFormula());
237
238            jmri.util.ThreadingUtil.runOnGUIEventually(() -> { setAppearanceComboBox(action); });
239        }
240
241        JComponent[] components = new JComponent[]{
242            _tabbedPaneNamedBean,
243            _tabbedPaneOperationType,
244            _tabbedPaneAppearanceType
245        };
246
247        List<JComponent> componentList = SwingConfiguratorInterface.parseMessage(
248                Bundle.getMessage("ActionSignalHead_Components"), components);
249
250        for (JComponent c : componentList) actionPanel.add(c);
251
252        panel.add(actionPanel);
253        panel.add(notePanel);
254        panel.add(examplePanel);
255
256        setGuiEnabledStates();
257    }
258
259
260    private void setGuiEnabledStates() {
261        _tabbedPaneAppearanceType.setEnabled(false);
262        _signalHeadAppearanceComboBox.setEnabled(false);
263        _signalHeadAppearanceReferenceTextField.setEnabled(false);
264        _signalHeadAppearanceLocalVariableTextField.setEnabled(false);
265        _signalHeadAppearanceFormulaTextField.setEnabled(false);
266        _exampleSignalHeadBeanPanel.getBeanCombo().setEnabled(false);
267
268        if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeDirect &&
269                _operationComboBox.getSelectedItem() != ActionSignalHead.OperationType.Appearance) {
270            return;
271        }
272
273        _tabbedPaneAppearanceType.setEnabled(true);
274
275        if (_selectNamedBeanSwing.getAddressing() != NamedBeanAddressing.Direct &&
276                _tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) {
277            _exampleSignalHeadBeanPanel.getBeanCombo().setEnabled(true);
278        }
279
280        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) {
281            _signalHeadAppearanceComboBox.setEnabled(true);
282        }
283        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeReference) {
284            _signalHeadAppearanceReferenceTextField.setEnabled(true);
285        }
286        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeLocalVariable) {
287            _signalHeadAppearanceLocalVariableTextField.setEnabled(true);
288        }
289        if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeFormula) {
290            _signalHeadAppearanceFormulaTextField.setEnabled(true);
291        }
292    }
293
294    private void setAppearanceComboBox(ActionSignalHead action) {
295        SignalHead sh;
296        if (_selectNamedBeanSwing.getAddressing() == NamedBeanAddressing.Direct) {
297            sh = _selectNamedBeanSwing.getBean();
298        } else {
299            sh = _exampleSignalHeadBeanPanel.getBeanCombo().getSelectedItem();
300        }
301
302        if (sh != null) {
303            _signalHeadAppearanceComboBox.removeAllItems();
304            int[] states = sh.getValidStates();
305            for (int s : states) {
306                SignalHeadAppearance sha = new SignalHeadAppearance();
307                sha._state = s;
308                sha._name = sh.getAppearanceName(s);
309                _signalHeadAppearanceComboBox.addItem(sha);
310                if (action != null) {
311                    if (action.getAppearance() == s) _signalHeadAppearanceComboBox.setSelectedItem(sha);
312                }
313            }
314            JComboBoxUtil.setupComboBoxMaxRows(_signalHeadAppearanceComboBox);
315        }
316    }
317
318
319    /** {@inheritDoc} */
320    @Override
321    public boolean validate(@Nonnull List<String> errorMessages) {
322        // Create a temporary action to test formula
323        ActionSignalHead action = new ActionSignalHead("IQDA1", null);
324
325        _selectNamedBeanSwing.validate(action.getSelectNamedBean(), errorMessages);
326
327        try {
328            if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) {
329                action.setOperationReference(_signalHeadOperationReferenceTextField.getText());
330            }
331        } catch (IllegalArgumentException e) {
332            errorMessages.add(e.getMessage());
333            return false;
334        }
335
336        return errorMessages.isEmpty();
337    }
338
339    /** {@inheritDoc} */
340    @Override
341    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
342        ActionSignalHead action = new ActionSignalHead(systemName, userName);
343        updateObject(action);
344        return InstanceManager.getDefault(DigitalActionManager.class).registerAction(action);
345    }
346
347    /** {@inheritDoc} */
348    @Override
349    public void updateObject(@Nonnull Base object) {
350        if (! (object instanceof ActionSignalHead)) {
351            throw new IllegalArgumentException("object must be an ActionSignalHead but is a: "+object.getClass().getName());
352        }
353        ActionSignalHead action = (ActionSignalHead)object;
354
355        _selectNamedBeanSwing.updateObject(action.getSelectNamedBean());
356
357        if (!_exampleSignalHeadBeanPanel.isEmpty()
358                && (_selectNamedBeanSwing.getAddressing() != NamedBeanAddressing.Direct)
359                && (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect)) {
360
361            SignalHead signalHead = _exampleSignalHeadBeanPanel.getNamedBean();
362            if (signalHead != null) {
363                NamedBeanHandle<SignalHead> handle
364                        = InstanceManager.getDefault(NamedBeanHandleManager.class)
365                                .getNamedBeanHandle(signalHead.getDisplayName(), signalHead);
366                action.getSelectExampleNamedBean().setNamedBean(handle);
367            }
368        } else {
369            action.getSelectExampleNamedBean().removeNamedBean();
370        }
371
372        try {
373            if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeDirect) {
374                action.setOperationAddressing(NamedBeanAddressing.Direct);
375                action.setOperationType((ActionSignalHead.OperationType)_operationComboBox.getSelectedItem());
376            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) {
377                action.setOperationAddressing(NamedBeanAddressing.Reference);
378                action.setOperationReference(_signalHeadOperationReferenceTextField.getText());
379            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeLocalVariable) {
380                action.setOperationAddressing(NamedBeanAddressing.LocalVariable);
381                action.setOperationLocalVariable(_signalHeadOperationLocalVariableTextField.getText());
382            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeFormula) {
383                action.setOperationAddressing(NamedBeanAddressing.Formula);
384                action.setOperationFormula(_signalHeadOperationFormulaTextField.getText());
385            } else {
386                throw new IllegalArgumentException("_tabbedPaneOperationType has unknown selection");
387            }
388
389            if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeDirect) {
390                action.setAppearanceAddressing(NamedBeanAddressing.Direct);
391
392                if (_signalHeadAppearanceComboBox.getItemCount() > 0) {
393                    action.setAppearance(_signalHeadAppearanceComboBox
394                            .getItemAt(_signalHeadAppearanceComboBox.getSelectedIndex())._state);
395                }
396            } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeReference) {
397                action.setAppearanceAddressing(NamedBeanAddressing.Reference);
398                action.setAppearanceReference(_signalHeadAppearanceReferenceTextField.getText());
399            } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeLocalVariable) {
400                action.setAppearanceAddressing(NamedBeanAddressing.LocalVariable);
401                action.setAppearanceLocalVariable(_signalHeadAppearanceLocalVariableTextField.getText());
402            } else if (_tabbedPaneAppearanceType.getSelectedComponent() == _panelAppearanceTypeFormula) {
403                action.setAppearanceAddressing(NamedBeanAddressing.Formula);
404                action.setAppearanceFormula(_signalHeadAppearanceFormulaTextField.getText());
405            } else {
406                throw new IllegalArgumentException("_tabbedPaneAppearanceType has unknown selection");
407            }
408        } catch (ParserException e) {
409            throw new RuntimeException("ParserException: "+e.getMessage(), e);
410        }
411    }
412
413    /** {@inheritDoc} */
414    @Override
415    public String toString() {
416        return Bundle.getMessage("SignalHead_Short");
417    }
418
419    @Override
420    public void dispose() {
421    }
422
423
424    private static class SignalHeadAppearance {
425
426        private int _state;
427        private String _name;
428
429        @Override
430        public String toString() {
431            return _name;
432        }
433
434    }
435
436//    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionSignalHeadSwing.class);
437
438}