001package jmri.jmrit.logixng.actions.swing;
002
003import java.awt.GridBagConstraints;
004import java.awt.GridBagLayout;
005import java.util.List;
006
007import javax.annotation.CheckForNull;
008import javax.annotation.Nonnull;
009import javax.swing.*;
010
011import jmri.*;
012import jmri.jmrit.logixng.*;
013import jmri.jmrit.logixng.actions.ActionDispatcher;
014import jmri.jmrit.logixng.actions.ActionDispatcher.DirectOperation;
015import jmri.jmrit.logixng.swing.SwingConfiguratorInterface;
016import jmri.jmrit.logixng.util.DispatcherActiveTrainManager;
017import jmri.jmrit.logixng.util.parser.ParserException;
018import jmri.jmrit.logixng.util.swing.LogixNG_SelectEnumSwing;
019import jmri.util.swing.JComboBoxUtil;
020
021/**
022 * Configures an ActionDispatcher object with a Swing JPanel.
023 *
024 * @author Daniel Bergqvist  Copyright 2021
025 * @author Dave Sand         Copyright 2021
026 */
027public class ActionDispatcherSwing extends AbstractDigitalActionSwing {
028
029    private JTabbedPane _tabbedPaneDispatcher;
030    private JPanel _panelDispatcherDirect;
031    private JPanel _panelDispatcherReference;
032    private JPanel _panelDispatcherLocalVariable;
033    private JPanel _panelDispatcherFormula;
034
035    private JComboBox<String> _fileNamesComboBox;
036    private JTextField _dispatcherReferenceTextField;
037    private JTextField _dispatcherLocalVariableTextField;
038    private JTextField _dispatcherFormulaTextField;
039
040    private LogixNG_SelectEnumSwing<DirectOperation> _selectOperationSwing;
041
042    private JTabbedPane _tabbedPaneData;
043    private JPanel _panelDataDirect;
044    private JPanel _panelDataReference;
045    private JPanel _panelDataLocalVariable;
046    private JPanel _panelDataFormula;
047
048    JLabel _priorityLabel;
049    JLabel _resetLabel;
050    JLabel _terminateLabel;
051    private JSpinner _priority;
052    private JCheckBox _resetOption;
053    private JCheckBox _terminateOption;
054    private JTextField _dispatcherDataReferenceTextField;
055    private JTextField _dispatcherDataLocalVariableTextField;
056    private JTextField _dispatcherDataFormulaTextField;
057
058
059    public ActionDispatcherSwing() {
060    }
061
062    public ActionDispatcherSwing(JDialog dialog) {
063        super.setJDialog(dialog);
064    }
065
066    @Override
067    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
068        ActionDispatcher action = (ActionDispatcher)object;
069
070        _selectOperationSwing = new LogixNG_SelectEnumSwing<>(getJDialog(), this);
071
072        panel = new JPanel();
073
074        // Left section
075        _tabbedPaneDispatcher = new JTabbedPane();
076        _panelDispatcherDirect = new javax.swing.JPanel();
077        _panelDispatcherReference = new javax.swing.JPanel();
078        _panelDispatcherLocalVariable = new javax.swing.JPanel();
079        _panelDispatcherFormula = new javax.swing.JPanel();
080
081        _tabbedPaneDispatcher.addTab(NamedBeanAddressing.Direct.toString(), _panelDispatcherDirect);
082        _tabbedPaneDispatcher.addTab(NamedBeanAddressing.Reference.toString(), _panelDispatcherReference);
083        _tabbedPaneDispatcher.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelDispatcherLocalVariable);
084        _tabbedPaneDispatcher.addTab(NamedBeanAddressing.Formula.toString(), _panelDispatcherFormula);
085
086        _fileNamesComboBox = new JComboBox<>();
087        InstanceManager.getDefault(DispatcherActiveTrainManager.class).getTrainInfoFileNames().forEach(name -> {
088            _fileNamesComboBox.addItem(name);
089        });
090        JComboBoxUtil.setupComboBoxMaxRows(_fileNamesComboBox);
091        _panelDispatcherDirect.add(_fileNamesComboBox);
092
093        _dispatcherReferenceTextField = new JTextField();
094        _dispatcherReferenceTextField.setColumns(30);
095        _panelDispatcherReference.add(_dispatcherReferenceTextField);
096
097        _dispatcherLocalVariableTextField = new JTextField();
098        _dispatcherLocalVariableTextField.setColumns(30);
099        _panelDispatcherLocalVariable.add(_dispatcherLocalVariableTextField);
100
101        _dispatcherFormulaTextField = new JTextField();
102        _dispatcherFormulaTextField.setColumns(30);
103        _panelDispatcherFormula.add(_dispatcherFormulaTextField);
104
105
106        // Center section
107        JPanel _tabbedPaneOperation;
108
109        if (action != null) {
110            _tabbedPaneOperation = _selectOperationSwing.createPanel(action.getSelectEnum(), DirectOperation.values());
111        } else {
112            _tabbedPaneOperation = _selectOperationSwing.createPanel(null, DirectOperation.values());
113        }
114
115
116        // Right section
117        _tabbedPaneData = new JTabbedPane();
118        _panelDataDirect = new javax.swing.JPanel();
119        _panelDataDirect.setLayout(new BoxLayout(_panelDataDirect, BoxLayout.Y_AXIS));
120        _panelDataReference = new javax.swing.JPanel();
121        _panelDataLocalVariable = new javax.swing.JPanel();
122        _panelDataFormula = new javax.swing.JPanel();
123
124        _tabbedPaneData.addTab(NamedBeanAddressing.Direct.toString(), _panelDataDirect);
125        _tabbedPaneData.addTab(NamedBeanAddressing.Reference.toString(), _panelDataReference);
126        _tabbedPaneData.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelDataLocalVariable);
127        _tabbedPaneData.addTab(NamedBeanAddressing.Formula.toString(), _panelDataFormula);
128
129
130        _priorityLabel = new JLabel(Bundle.getMessage("ActionDispatcher_Priority"));
131        _priority = new JSpinner(new SpinnerNumberModel(5, 0, 100, 1));
132        _resetLabel = new JLabel(Bundle.getMessage("ActionDispatcher_Reset"));
133        _resetOption = new JCheckBox();
134        _terminateLabel = new JLabel(Bundle.getMessage("ActionDispatcher_Terminate"));
135        _terminateOption = new JCheckBox();
136
137        JPanel dataGroup = new JPanel();
138        dataGroup.setLayout(new GridBagLayout());
139        GridBagConstraints constraint = new GridBagConstraints();
140        constraint.gridwidth = 1;
141        constraint.gridheight = 1;
142        constraint.gridx = 0;
143        constraint.gridy = 0;
144        constraint.anchor = GridBagConstraints.EAST;
145        dataGroup.add(_priorityLabel, constraint);
146        _priorityLabel.setLabelFor(_priority);
147        constraint.gridy = 1;
148        dataGroup.add(_resetLabel, constraint);
149        _resetLabel.setLabelFor(_resetOption);
150        constraint.gridy = 2;
151        dataGroup.add(_terminateLabel, constraint);
152        _terminateLabel.setLabelFor(_terminateOption);
153        constraint.gridx = 1;
154        constraint.gridy = 0;
155        constraint.anchor = GridBagConstraints.WEST;
156        dataGroup.add(_priority, constraint);
157        constraint.gridy = 1;
158        dataGroup.add(_resetOption, constraint);
159        constraint.gridy = 2;
160        dataGroup.add(_terminateOption, constraint);
161
162        _panelDataDirect.add(dataGroup);
163
164        _dispatcherDataReferenceTextField = new JTextField();
165        _dispatcherDataReferenceTextField.setColumns(30);
166        _panelDataReference.add(_dispatcherDataReferenceTextField);
167
168        _dispatcherDataLocalVariableTextField = new JTextField();
169        _dispatcherDataLocalVariableTextField.setColumns(30);
170        _panelDataLocalVariable.add(_dispatcherDataLocalVariableTextField);
171
172        _dispatcherDataFormulaTextField = new JTextField();
173        _dispatcherDataFormulaTextField.setColumns(30);
174        _panelDataFormula.add(_dispatcherDataFormulaTextField);
175
176
177        if (action != null) {
178            switch (action.getAddressing()) {
179                case Direct: _tabbedPaneDispatcher.setSelectedComponent(_panelDispatcherDirect); break;
180                case Reference: _tabbedPaneDispatcher.setSelectedComponent(_panelDispatcherReference); break;
181                case LocalVariable: _tabbedPaneDispatcher.setSelectedComponent(_panelDispatcherLocalVariable); break;
182                case Formula: _tabbedPaneDispatcher.setSelectedComponent(_panelDispatcherFormula); break;
183                default: throw new IllegalArgumentException("invalid _addressing state: " + action.getAddressing().name());
184            }
185            _fileNamesComboBox.setSelectedItem(action.getTrainInfoFileName());
186            _dispatcherReferenceTextField.setText(action.getReference());
187            _dispatcherLocalVariableTextField.setText(action.getLocalVariable());
188            _dispatcherFormulaTextField.setText(action.getFormula());
189
190            switch (action.getDataAddressing()) {
191                case Direct: _tabbedPaneData.setSelectedComponent(_panelDataDirect); break;
192                case Reference: _tabbedPaneData.setSelectedComponent(_panelDataReference); break;
193                case LocalVariable: _tabbedPaneData.setSelectedComponent(_panelDataLocalVariable); break;
194                case Formula: _tabbedPaneData.setSelectedComponent(_panelDataFormula); break;
195                default: throw new IllegalArgumentException("invalid _addressing state: " + action.getDataAddressing().name());
196            }
197            _priority.setValue(action.getTrainPriority());
198            _resetOption.setSelected(action.getResetOption());
199            _terminateOption.setSelected(action.getTerminateOption());
200            _dispatcherDataReferenceTextField.setText(action.getDataReference());
201            _dispatcherDataLocalVariableTextField.setText(action.getDataLocalVariable());
202            _dispatcherDataFormulaTextField.setText(action.getDataFormula());
203        }
204
205        setDataPanelState();
206
207        _selectOperationSwing.addAddressingListener((evt) -> { setDataPanelState(); });
208        _selectOperationSwing.addEnumListener((evt) -> { setDataPanelState(); });
209
210        JComponent[] components = new JComponent[]{
211            _tabbedPaneDispatcher,
212            _tabbedPaneOperation,
213            _tabbedPaneData};
214
215        List<JComponent> componentList = SwingConfiguratorInterface.parseMessage(
216                Bundle.getMessage("ActionDispatcher_Components"), components);
217
218        for (JComponent c : componentList) panel.add(c);
219    }
220
221    private void setDataPanelState() {
222
223        _priorityLabel.setVisible(false);
224        _priority.setVisible(false);
225        _resetLabel.setVisible(false);
226        _resetOption.setVisible(false);
227        _terminateLabel.setVisible(false);
228        _terminateOption.setVisible(false);
229
230        boolean newState = false;
231
232        if (_selectOperationSwing.isEnumSelectedOrIndirectAddressing(
233                DirectOperation.TrainPriority)) {
234            _priorityLabel.setVisible(true);
235            _priority.setVisible(true);
236            newState = true;
237        }
238
239        if (_selectOperationSwing.isEnumSelectedOrIndirectAddressing(
240                DirectOperation.ResetWhenDoneOption)) {
241            _resetLabel.setVisible(true);
242            _resetOption.setVisible(true);
243            newState = true;
244        }
245
246        if (_selectOperationSwing.isEnumSelectedOrIndirectAddressing(
247                DirectOperation.TerminateWhenDoneOption)) {
248            _terminateLabel.setVisible(true);
249            _terminateOption.setVisible(true);
250            newState = true;
251        }
252
253        _tabbedPaneData.setEnabled(newState);
254        _dispatcherDataReferenceTextField.setEnabled(newState);
255        _dispatcherDataLocalVariableTextField.setEnabled(newState);
256        _dispatcherDataFormulaTextField.setEnabled(newState);
257   }
258
259    /** {@inheritDoc} */
260    @Override
261    public boolean validate(@Nonnull List<String> errorMessages) {
262        // Create a temporary action to test formula
263        ActionDispatcher action = new ActionDispatcher("IQDA2", null);
264
265        validateInfoFileSection(errorMessages);
266        _selectOperationSwing.validate(action.getSelectEnum(), errorMessages);
267
268        if ((_selectOperationSwing.getAddressing() == NamedBeanAddressing.Direct)
269                && (_selectOperationSwing.getEnum() == DirectOperation.None)) {
270            errorMessages.add(Bundle.getMessage("ActionDispatcher_ErrorDispatcherActionNotSelected"));
271        }
272        validateDataSection(errorMessages);
273        return errorMessages.isEmpty();
274    }
275
276    private void validateInfoFileSection(List<String> errorMessages) {
277        // Create a temporary action to test formula
278        ActionDispatcher action = new ActionDispatcher("IQDA1", null);
279
280        try {
281            if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherReference) {
282                action.setReference(_dispatcherReferenceTextField.getText());
283            }
284        } catch (IllegalArgumentException e) {
285            errorMessages.add(e.getMessage());
286            return;
287        }
288
289        try {
290            action.setFormula(_dispatcherFormulaTextField.getText());
291            if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherDirect) {
292                action.setAddressing(NamedBeanAddressing.Direct);
293            } else if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherReference) {
294                action.setAddressing(NamedBeanAddressing.Reference);
295            } else if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherLocalVariable) {
296                action.setAddressing(NamedBeanAddressing.LocalVariable);
297            } else if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherFormula) {
298                action.setAddressing(NamedBeanAddressing.Formula);
299            } else {
300                throw new IllegalArgumentException("_tabbedPane has unknown selection");
301            }
302        } catch (ParserException e) {
303            errorMessages.add("Cannot parse formula: " + e.getMessage());
304        }
305
306//        if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherDirect) {
307//            if (_stateComboBox.getSelectedItem() == null) {
308//                errorMessages.add(Bundle.getMessage("ActionDispatcher_ErrorFile"));
309//            }
310//        }
311    }
312
313    private void validateDataSection(List<String> errorMessages) {
314        // Create a temporary action to test formula
315        ActionDispatcher action = new ActionDispatcher("IQDA3", null);
316
317        try {
318            if (_tabbedPaneData.getSelectedComponent() == _panelDataReference) {
319                action.setDataReference(_dispatcherDataReferenceTextField.getText());
320            }
321        } catch (IllegalArgumentException e) {
322            errorMessages.add(e.getMessage());
323            return;
324        }
325
326        try {
327            action.setDataFormula(_dispatcherDataFormulaTextField.getText());
328            if (_tabbedPaneData.getSelectedComponent() == _panelDataDirect) {
329                action.setDataAddressing(NamedBeanAddressing.Direct);
330            } else if (_tabbedPaneData.getSelectedComponent() == _panelDataReference) {
331                action.setDataAddressing(NamedBeanAddressing.Reference);
332            } else if (_tabbedPaneData.getSelectedComponent() == _panelDataLocalVariable) {
333                action.setDataAddressing(NamedBeanAddressing.LocalVariable);
334            } else if (_tabbedPaneData.getSelectedComponent() == _panelDataFormula) {
335                action.setDataAddressing(NamedBeanAddressing.Formula);
336            } else {
337                throw new IllegalArgumentException("_tabbedPane has unknown selection");
338            }
339        } catch (ParserException e) {
340            errorMessages.add("Cannot parse formula: " + e.getMessage());
341        }
342
343    }
344
345    /** {@inheritDoc} */
346    @Override
347    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
348        ActionDispatcher action = new ActionDispatcher(systemName, userName);
349        updateObject(action);
350        return InstanceManager.getDefault(DigitalActionManager.class).registerAction(action);
351    }
352
353    /** {@inheritDoc} */
354    @Override
355    public void updateObject(@Nonnull Base object) {
356        if (! (object instanceof ActionDispatcher)) {
357            throw new IllegalArgumentException("object must be an ActionDispatcher but is a: "+object.getClass().getName());
358        }
359        ActionDispatcher action = (ActionDispatcher) object;
360
361        try {
362            // Left section
363            if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherDirect) {
364                action.setAddressing(NamedBeanAddressing.Direct);
365                action.setTrainInfoFileName((String) _fileNamesComboBox.getSelectedItem());
366            } else if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherReference) {
367                action.setAddressing(NamedBeanAddressing.Reference);
368                action.setReference(_dispatcherReferenceTextField.getText());
369            } else if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherLocalVariable) {
370                action.setAddressing(NamedBeanAddressing.LocalVariable);
371                action.setLocalVariable(_dispatcherLocalVariableTextField.getText());
372            } else if (_tabbedPaneDispatcher.getSelectedComponent() == _panelDispatcherFormula) {
373                action.setAddressing(NamedBeanAddressing.Formula);
374                action.setFormula(_dispatcherFormulaTextField.getText());
375            } else {
376                throw new IllegalArgumentException("_tabbedPaneDispatcher has unknown selection");
377            }
378
379            // Center section
380            _selectOperationSwing.updateObject(action.getSelectEnum());
381
382            // Right section
383            if (_tabbedPaneData.getSelectedComponent() == _panelDataDirect) {
384                action.setDataAddressing(NamedBeanAddressing.Direct);
385                action.setTrainPriority((int) _priority.getValue());
386                action.setResetOption(_resetOption.isSelected());
387                action.setTerminateOption(_terminateOption.isSelected());
388            } else if (_tabbedPaneData.getSelectedComponent() == _panelDataReference) {
389                action.setDataAddressing(NamedBeanAddressing.Reference);
390                action.setDataReference(_dispatcherDataReferenceTextField.getText());
391            } else if (_tabbedPaneData.getSelectedComponent() == _panelDataLocalVariable) {
392                action.setDataAddressing(NamedBeanAddressing.LocalVariable);
393                action.setDataLocalVariable(_dispatcherDataLocalVariableTextField.getText());
394            } else if (_tabbedPaneData.getSelectedComponent() == _panelDataFormula) {
395                action.setDataAddressing(NamedBeanAddressing.Formula);
396                action.setDataFormula(_dispatcherDataFormulaTextField.getText());
397            } else {
398                throw new IllegalArgumentException("_tabbedPaneData has unknown selection");
399            }
400        } catch (ParserException e) {
401            throw new RuntimeException("ParserException: "+e.getMessage(), e);
402        }
403    }
404
405    /** {@inheritDoc} */
406    @Override
407    public String toString() {
408        return Bundle.getMessage("ActionDispatcher_Short");
409    }
410
411    @Override
412    public void setDefaultValues() {
413        _selectOperationSwing.setEnum(DirectOperation.LoadTrainFromFile);
414    }
415
416    @Override
417    public void dispose() {
418        _selectOperationSwing.dispose();
419    }
420
421
422//     private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionDispatcherSwing.class);
423
424}