001package jmri.jmrit.logixng.actions.swing;
002
003import java.awt.event.ActionEvent;
004import java.util.List;
005
006import javax.annotation.CheckForNull;
007import javax.annotation.Nonnull;
008import javax.swing.*;
009
010import jmri.InstanceManager;
011import jmri.jmrit.logixng.*;
012import jmri.jmrit.logixng.actions.ActionScript;
013import jmri.jmrit.logixng.swing.SwingConfiguratorInterface;
014import jmri.jmrit.logixng.util.parser.ParserException;
015import jmri.script.ScriptEngineSelector;
016import jmri.script.swing.ScriptEngineSelectorSwing;
017import jmri.script.swing.ScriptFileChooser;
018import jmri.util.FileUtil;
019import jmri.util.swing.JComboBoxUtil;
020
021/**
022 * Configures an ActionScript object with a Swing JPanel.
023 *
024 * @author Daniel Bergqvist 2021
025 */
026public class ActionScriptSwing extends AbstractDigitalActionSwing {
027
028    public static final int NUM_COLUMNS_TEXT_FIELDS = 20;
029
030    private JTabbedPane _tabbedPaneOperationType;
031    private JPanel _panelOperationTypeDirect;
032    private JPanel _panelOperationTypeReference;
033    private JPanel _panelOperationTypeLocalVariable;
034    private JPanel _panelOperationTypeFormula;
035
036    private JComboBox<ActionScript.OperationType> _operationComboBox;
037    private JTextField _scriptOperationReferenceTextField;
038    private JTextField _scriptOperationLocalVariableTextField;
039    private JTextField _scriptOperationFormulaTextField;
040
041    private JTabbedPane _tabbedPaneScriptType;
042    private JPanel _panelScriptTypeDirect;
043    private JPanel _panelScriptTypeReference;
044    private JPanel _panelScriptTypeLocalVariable;
045    private JPanel _panelScriptTypeFormula;
046
047    private ScriptFileChooser scriptFileChooser;
048    private JTextField _scriptTextField;
049    private JTextField _scriptReferenceTextField;
050    private JTextField _scriptLocalVariableTextField;
051    private JTextField _scriptFormulaTextField;
052
053    private ScriptEngineSelectorSwing _scriptEngineSelectorSwing;
054
055
056    @Override
057    protected void createPanel(@CheckForNull Base object, @Nonnull JPanel buttonPanel) {
058        ActionScript action = (ActionScript)object;
059
060        if (action != null) {
061            _scriptEngineSelectorSwing = new ScriptEngineSelectorSwing(action.getScriptEngineSelector());
062        } else {
063            _scriptEngineSelectorSwing = new ScriptEngineSelectorSwing(new ScriptEngineSelector());
064        }
065
066        panel = new JPanel();
067        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
068
069        JPanel actionPanel = new JPanel();
070
071
072        // Set up the tabbed pane for selecting the operation
073        _tabbedPaneOperationType = new JTabbedPane();
074        _panelOperationTypeDirect = new javax.swing.JPanel();
075        _panelOperationTypeDirect.setLayout(new BoxLayout(_panelOperationTypeDirect, BoxLayout.Y_AXIS));
076        _panelOperationTypeReference = new javax.swing.JPanel();
077        _panelOperationTypeReference.setLayout(new BoxLayout(_panelOperationTypeReference, BoxLayout.Y_AXIS));
078        _panelOperationTypeLocalVariable = new javax.swing.JPanel();
079        _panelOperationTypeLocalVariable.setLayout(new BoxLayout(_panelOperationTypeLocalVariable, BoxLayout.Y_AXIS));
080        _panelOperationTypeFormula = new javax.swing.JPanel();
081        _panelOperationTypeFormula.setLayout(new BoxLayout(_panelOperationTypeFormula, BoxLayout.Y_AXIS));
082
083        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Direct.toString(), _panelOperationTypeDirect);
084        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Reference.toString(), _panelOperationTypeReference);
085        _tabbedPaneOperationType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelOperationTypeLocalVariable);
086        _tabbedPaneOperationType.addTab(NamedBeanAddressing.Formula.toString(), _panelOperationTypeFormula);
087
088        _operationComboBox = new JComboBox<>();
089        for (ActionScript.OperationType e : ActionScript.OperationType.values()) {
090            _operationComboBox.addItem(e);
091        }
092        JComboBoxUtil.setupComboBoxMaxRows(_operationComboBox);
093        _panelOperationTypeDirect.add(new JLabel(Bundle.getMessage("ActionScript_Operation")));
094        _panelOperationTypeDirect.add(_operationComboBox);
095
096        _scriptOperationReferenceTextField = new JTextField();
097        _scriptOperationReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
098        _panelOperationTypeReference.add(new JLabel(Bundle.getMessage("ActionScript_Operation")));
099        _panelOperationTypeReference.add(_scriptOperationReferenceTextField);
100
101        _scriptOperationLocalVariableTextField = new JTextField();
102        _scriptOperationLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
103        _panelOperationTypeLocalVariable.add(new JLabel(Bundle.getMessage("ActionScript_Operation")));
104        _panelOperationTypeLocalVariable.add(_scriptOperationLocalVariableTextField);
105
106        _scriptOperationFormulaTextField = new JTextField();
107        _scriptOperationFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
108        _panelOperationTypeFormula.add(new JLabel(Bundle.getMessage("ActionScript_Operation")));
109        _panelOperationTypeFormula.add(_scriptOperationFormulaTextField);
110
111
112        // Set up the tabbed pane for selecting the appearance
113        _tabbedPaneScriptType = new JTabbedPane();
114        _panelScriptTypeDirect = new javax.swing.JPanel();
115//        _panelScriptTypeDirect.setLayout(new BoxLayout(_panelScriptTypeDirect, BoxLayout.Y_AXIS));
116        _panelScriptTypeReference = new javax.swing.JPanel();
117        _panelScriptTypeReference.setLayout(new BoxLayout(_panelScriptTypeReference, BoxLayout.Y_AXIS));
118        _panelScriptTypeLocalVariable = new javax.swing.JPanel();
119        _panelScriptTypeLocalVariable.setLayout(new BoxLayout(_panelScriptTypeLocalVariable, BoxLayout.Y_AXIS));
120        _panelScriptTypeFormula = new javax.swing.JPanel();
121        _panelScriptTypeFormula.setLayout(new BoxLayout(_panelScriptTypeFormula, BoxLayout.Y_AXIS));
122
123        _tabbedPaneScriptType.addTab(NamedBeanAddressing.Direct.toString(), _panelScriptTypeDirect);
124        _tabbedPaneScriptType.addTab(NamedBeanAddressing.Reference.toString(), _panelScriptTypeReference);
125        _tabbedPaneScriptType.addTab(NamedBeanAddressing.LocalVariable.toString(), _panelScriptTypeLocalVariable);
126        _tabbedPaneScriptType.addTab(NamedBeanAddressing.Formula.toString(), _panelScriptTypeFormula);
127
128        JButton _actionSelectFileButton = new JButton("..."); // "File" replaced by ...
129        _actionSelectFileButton.setMaximumSize(_actionSelectFileButton.getPreferredSize());
130        _actionSelectFileButton.setToolTipText(Bundle.getMessage("FileButtonHint"));  // NOI18N
131        _actionSelectFileButton.addActionListener((ActionEvent e) -> {
132            scriptFileChooser = new ScriptFileChooser(FileUtil.getScriptsPath());
133            scriptFileChooser.rescanCurrentDirectory();
134            int retVal = scriptFileChooser.showOpenDialog(null);
135            // handle selection or cancel
136            if (retVal == JFileChooser.APPROVE_OPTION) {
137                // set selected file location
138                try {
139                    _scriptTextField.setText(FileUtil.getPortableFilename(scriptFileChooser.getSelectedFile().getCanonicalPath()));
140                } catch (java.io.IOException ex) {
141                    log.error("exception setting file location", ex);  // NOI18N
142                    _scriptTextField.setText("");
143                }
144            }
145        });
146        _panelScriptTypeDirect.add(_actionSelectFileButton);
147        JPanel _scriptTextPanel = new JPanel();
148        _scriptTextPanel.setLayout(new BoxLayout(_scriptTextPanel, BoxLayout.Y_AXIS));
149        _scriptTextField = new JTextField(30);
150        _scriptTextPanel.add(new JLabel(Bundle.getMessage("ActionScript_Script")));
151        _scriptTextPanel.add(_scriptTextField);
152        _panelScriptTypeDirect.add(_scriptTextPanel);
153
154        _scriptReferenceTextField = new JTextField();
155        _scriptReferenceTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
156        _panelScriptTypeReference.add(new JLabel(Bundle.getMessage("ActionScript_Script")));
157        _panelScriptTypeReference.add(_scriptReferenceTextField);
158
159        _scriptLocalVariableTextField = new JTextField();
160        _scriptLocalVariableTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
161        _panelScriptTypeLocalVariable.add(new JLabel(Bundle.getMessage("ActionScript_Script")));
162        _panelScriptTypeLocalVariable.add(_scriptLocalVariableTextField);
163
164        _scriptFormulaTextField = new JTextField();
165        _scriptFormulaTextField.setColumns(NUM_COLUMNS_TEXT_FIELDS);
166        _panelScriptTypeFormula.add(new JLabel(Bundle.getMessage("ActionScript_Script")));
167        _panelScriptTypeFormula.add(_scriptFormulaTextField);
168
169
170        if (action != null) {
171            switch (action.getOperationAddressing()) {
172                case Direct: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeDirect); break;
173                case Reference: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeReference); break;
174                case LocalVariable: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeLocalVariable); break;
175                case Formula: _tabbedPaneOperationType.setSelectedComponent(_panelOperationTypeFormula); break;
176                default: throw new IllegalArgumentException("invalid _addressing state: " + action.getOperationAddressing().name());
177            }
178            _operationComboBox.setSelectedItem(action.getOperationType());
179            _scriptOperationReferenceTextField.setText(action.getOperationReference());
180            _scriptOperationLocalVariableTextField.setText(action.getOperationLocalVariable());
181            _scriptOperationFormulaTextField.setText(action.getOperationFormula());
182
183            switch (action.getScriptAddressing()) {
184                case Direct: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeDirect); break;
185                case Reference: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeReference); break;
186                case LocalVariable: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeLocalVariable); break;
187                case Formula: _tabbedPaneScriptType.setSelectedComponent(_panelScriptTypeFormula); break;
188                default: throw new IllegalArgumentException("invalid _addressing state: " + action.getScriptAddressing().name());
189            }
190            _scriptTextField.setText(action.getScript());
191            _scriptReferenceTextField.setText(action.getScriptReference());
192            _scriptLocalVariableTextField.setText(action.getScriptLocalVariable());
193            _scriptFormulaTextField.setText(action.getScriptFormula());
194        }
195
196        JComponent[] components = new JComponent[]{
197            _tabbedPaneOperationType,
198            _tabbedPaneScriptType
199        };
200
201        List<JComponent> componentList = SwingConfiguratorInterface.parseMessage(
202                Bundle.getMessage("ActionScript_Components"), components);
203
204        for (JComponent c : componentList) actionPanel.add(c);
205        panel.add(actionPanel);
206
207        JPanel scriptSelectorPanel = new JPanel();
208        scriptSelectorPanel.add(new JLabel(Bundle.getMessage("ActionScript_ScriptSelector")));
209        scriptSelectorPanel.add(_scriptEngineSelectorSwing.getComboBox());
210
211        panel.add(scriptSelectorPanel);
212    }
213
214    /** {@inheritDoc} */
215    @Override
216    public boolean validate(@Nonnull List<String> errorMessages) {
217        // Create a temporary action to test formula
218        ActionScript action = new ActionScript("IQDA1", null);
219
220        try {
221            if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) {
222                action.setOperationReference(_scriptOperationReferenceTextField.getText());
223            }
224        } catch (IllegalArgumentException e) {
225            errorMessages.add(e.getMessage());
226            return false;
227        }
228
229        try {
230            action.setScriptFormula(_scriptFormulaTextField.getText());
231            if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeDirect) {
232                action.setScriptAddressing(NamedBeanAddressing.Direct);
233            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeReference) {
234                action.setScriptAddressing(NamedBeanAddressing.Reference);
235            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeLocalVariable) {
236                action.setScriptAddressing(NamedBeanAddressing.LocalVariable);
237            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeFormula) {
238                action.setScriptAddressing(NamedBeanAddressing.Formula);
239            } else {
240                throw new IllegalArgumentException("_tabbedPaneScriptType has unknown selection");
241            }
242        } catch (ParserException e) {
243            errorMessages.add("Cannot parse formula: " + e.getMessage());
244            return false;
245        }
246        return true;
247    }
248
249    /** {@inheritDoc} */
250    @Override
251    public MaleSocket createNewObject(@Nonnull String systemName, @CheckForNull String userName) {
252        ActionScript action = new ActionScript(systemName, userName);
253        updateObject(action);
254        return InstanceManager.getDefault(DigitalActionManager.class).registerAction(action);
255    }
256
257    /** {@inheritDoc} */
258    @Override
259    public void updateObject(@Nonnull Base object) {
260        if (! (object instanceof ActionScript)) {
261            throw new IllegalArgumentException("object must be an ActionScript but is a: "+object.getClass().getName());
262        }
263        ActionScript action = (ActionScript)object;
264
265        try {
266            if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeDirect) {
267                action.setOperationAddressing(NamedBeanAddressing.Direct);
268                action.setOperationType((ActionScript.OperationType)_operationComboBox.getSelectedItem());
269            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeReference) {
270                action.setOperationAddressing(NamedBeanAddressing.Reference);
271                action.setOperationReference(_scriptOperationReferenceTextField.getText());
272            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeLocalVariable) {
273                action.setOperationAddressing(NamedBeanAddressing.LocalVariable);
274                action.setOperationLocalVariable(_scriptOperationLocalVariableTextField.getText());
275            } else if (_tabbedPaneOperationType.getSelectedComponent() == _panelOperationTypeFormula) {
276                action.setOperationAddressing(NamedBeanAddressing.Formula);
277                action.setOperationFormula(_scriptOperationFormulaTextField.getText());
278            } else {
279                throw new IllegalArgumentException("_tabbedPaneOperationType has unknown selection");
280            }
281
282            if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeDirect) {
283                action.setScriptAddressing(NamedBeanAddressing.Direct);
284                action.setScript(_scriptTextField.getText());
285            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeReference) {
286                action.setScriptAddressing(NamedBeanAddressing.Reference);
287                action.setScriptReference(_scriptReferenceTextField.getText());
288            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeLocalVariable) {
289                action.setScriptAddressing(NamedBeanAddressing.LocalVariable);
290                action.setScriptLocalVariable(_scriptLocalVariableTextField.getText());
291            } else if (_tabbedPaneScriptType.getSelectedComponent() == _panelScriptTypeFormula) {
292                action.setScriptAddressing(NamedBeanAddressing.Formula);
293                action.setScriptFormula(_scriptFormulaTextField.getText());
294            } else {
295                throw new IllegalArgumentException("_tabbedPaneAspectType has unknown selection");
296            }
297        } catch (ParserException e) {
298            throw new RuntimeException("ParserException: "+e.getMessage(), e);
299        }
300
301        _scriptEngineSelectorSwing.update();
302    }
303
304    /** {@inheritDoc} */
305    @Override
306    public String toString() {
307        return Bundle.getMessage("ActionScript_Short");
308    }
309
310    @Override
311    public void dispose() {
312    }
313
314
315    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionScriptSwing.class);
316
317}