001package jmri.jmrit.operations.trains.tools;
002
003import java.awt.GridBagLayout;
004import java.io.File;
005import java.util.List;
006
007import javax.swing.*;
008
009import jmri.InstanceManager;
010import jmri.jmrit.operations.OperationsFrame;
011import jmri.jmrit.operations.OperationsXml;
012import jmri.jmrit.operations.setup.Setup;
013import jmri.jmrit.operations.trains.Train;
014import jmri.jmrit.operations.trains.TrainEditFrame;
015import jmri.jmrit.operations.trains.TrainManager;
016import jmri.jmrit.operations.trains.TrainManagerXml;
017import jmri.script.JmriScriptEngineManager;
018import jmri.script.swing.ScriptFileChooser;
019import jmri.util.FileUtil;
020import jmri.util.swing.JmriJOptionPane;
021
022/**
023 * Frame for user edit of a train's script options. Allows the user to execute
024 * scripts when a train is built, moved or terminated.
025 *
026 * @author Bob Jacobsen Copyright (C) 2004
027 * @author Dan Boudreau Copyright (C) 2010, 2011, 2013
028 */
029public class TrainScriptFrame extends OperationsFrame {
030
031    TrainManager manager;
032    TrainManagerXml managerXml;
033
034    Train _train = null;
035
036    // script panels
037    JPanel pBuildScript = new JPanel();
038    JPanel pAfterBuildScript = new JPanel();
039    JPanel pMoveScript = new JPanel();
040    JPanel pTerminationScript = new JPanel();
041    JScrollPane buildScriptPane;
042    JScrollPane afterBuildScriptPane;
043    JScrollPane moveScriptPane;
044    JScrollPane terminationScriptPane;
045
046    // labels
047    JLabel trainName = new JLabel();
048    JLabel trainDescription = new JLabel();
049
050    // major buttons
051    JButton addBuildScriptButton = new JButton(Bundle.getMessage("AddScript"));
052    JButton addAfterBuildScriptButton = new JButton(Bundle.getMessage("AddScript"));
053    JButton addMoveScriptButton = new JButton(Bundle.getMessage("AddScript"));
054    JButton addTerminationScriptButton = new JButton(Bundle.getMessage("AddScript"));
055
056    JButton runBuildScriptButton = new JButton(Bundle.getMessage("RunScripts"));
057    JButton runAfterBuildScriptButton = new JButton(Bundle.getMessage("RunScripts"));
058    JButton runMoveScriptButton = new JButton(Bundle.getMessage("RunScripts"));
059    JButton runTerminationScriptButton = new JButton(Bundle.getMessage("RunScripts"));
060
061    JButton saveTrainButton = new JButton(Bundle.getMessage("SaveTrain"));
062
063    public TrainScriptFrame() {
064        super(Bundle.getMessage("MenuItemScripts"));
065    }
066
067    public void initComponents(TrainEditFrame parent) {
068        // Set up script options in a Scroll Pane..
069        buildScriptPane = new JScrollPane(pBuildScript);
070        buildScriptPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
071        buildScriptPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("ScriptsBeforeBuild")));
072
073        afterBuildScriptPane = new JScrollPane(pAfterBuildScript);
074        afterBuildScriptPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
075        afterBuildScriptPane.setBorder(BorderFactory.createTitledBorder(Bundle
076                .getMessage("ScriptsAfterBuild")));
077
078        moveScriptPane = new JScrollPane(pMoveScript);
079        moveScriptPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
080        moveScriptPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("ScriptsWhenMoved")));
081
082        terminationScriptPane = new JScrollPane(pTerminationScript);
083        terminationScriptPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
084        terminationScriptPane.setBorder(BorderFactory.createTitledBorder(Bundle
085                .getMessage("ScriptsWhenTerminated")));
086
087        // remember who called us
088        parent.setChildFrame(this);
089        _train = parent._train;
090
091        // load managers
092        manager = InstanceManager.getDefault(TrainManager.class);
093        managerXml = InstanceManager.getDefault(TrainManagerXml.class);
094
095        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
096
097        // Layout the panel by rows
098        JPanel p1 = new JPanel();
099        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
100
101        // row 1a
102        JPanel pName = new JPanel();
103        pName.setLayout(new GridBagLayout());
104        pName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Name")));
105        addItem(pName, trainName, 0, 0);
106
107        // row 1b
108        JPanel pDesc = new JPanel();
109        pDesc.setLayout(new GridBagLayout());
110        pDesc.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Description")));
111        addItem(pDesc, trainDescription, 0, 0);
112
113        p1.add(pName);
114        p1.add(pDesc);
115
116        // row 2
117        updateBuildScriptPanel();
118
119        // row 3
120        updateAfterBuildScriptPanel();
121
122        // row 4
123        updateMoveScriptPanel();
124
125        // row 6
126        updateTerminationScriptPanel();
127
128        // row 8 buttons
129        JPanel pB = new JPanel();
130        pB.setLayout(new GridBagLayout());
131        addItem(pB, saveTrainButton, 3, 0);
132
133        getContentPane().add(p1);
134        getContentPane().add(buildScriptPane);
135        getContentPane().add(afterBuildScriptPane);
136        getContentPane().add(moveScriptPane);
137        getContentPane().add(terminationScriptPane);
138        getContentPane().add(pB);
139
140        // setup buttons
141        addButtonAction(addBuildScriptButton);
142        addButtonAction(addAfterBuildScriptButton);
143        addButtonAction(addMoveScriptButton);
144        addButtonAction(addTerminationScriptButton);
145        addButtonAction(runBuildScriptButton);
146        addButtonAction(runAfterBuildScriptButton);
147        addButtonAction(runMoveScriptButton);
148        addButtonAction(runTerminationScriptButton);
149        addButtonAction(saveTrainButton);
150
151        if (_train != null) {
152            trainName.setText(_train.getName());
153            trainDescription.setText(_train.getDescription());
154            enableButtons(true);
155        } else {
156            enableButtons(false);
157        }
158        addHelpMenu("package.jmri.jmrit.operations.Operations_TrainScripts", true); // NOI18N
159        initMinimumSize();
160    }
161
162    private void updateBuildScriptPanel() {
163        pBuildScript.removeAll();
164        pBuildScript.setLayout(new GridBagLayout());
165        addItem(pBuildScript, addBuildScriptButton, 0, 0);
166
167        // load any existing train build scripts
168        if (_train != null) {
169            List<String> scripts = _train.getBuildScripts();
170            if (scripts.size() > 0) {
171                addItem(pBuildScript, runBuildScriptButton, 1, 0);
172            }
173            for (int i = 0; i < scripts.size(); i++) {
174                JButton removeBuildScripts = new JButton(Bundle.getMessage("RemoveScript"));
175                removeBuildScripts.setName(scripts.get(i));
176                removeBuildScripts.addActionListener(new java.awt.event.ActionListener() {
177                    @Override
178                    public void actionPerformed(java.awt.event.ActionEvent e) {
179                        buttonActionRemoveBuildScript(e);
180                    }
181                });
182                addButtonAction(removeBuildScripts);
183                JLabel pathname = new JLabel(scripts.get(i));
184                addItem(pBuildScript, removeBuildScripts, 0, i + 1);
185                addItem(pBuildScript, pathname, 1, i + 1);
186            }
187        }
188    }
189
190    private void updateAfterBuildScriptPanel() {
191        pAfterBuildScript.removeAll();
192        pAfterBuildScript.setLayout(new GridBagLayout());
193        addItem(pAfterBuildScript, addAfterBuildScriptButton, 0, 0);
194
195        // load any existing train build scripts
196        if (_train != null) {
197            List<String> scripts = _train.getAfterBuildScripts();
198            if (scripts.size() > 0) {
199                addItem(pAfterBuildScript, runAfterBuildScriptButton, 1, 0);
200            }
201            for (int i = 0; i < scripts.size(); i++) {
202                JButton removeAfterBuildScripts = new JButton(Bundle.getMessage("RemoveScript"));
203                removeAfterBuildScripts.setName(scripts.get(i));
204                removeAfterBuildScripts.addActionListener(new java.awt.event.ActionListener() {
205                    @Override
206                    public void actionPerformed(java.awt.event.ActionEvent e) {
207                        buttonActionRemoveAfterBuildScript(e);
208                    }
209                });
210                addButtonAction(removeAfterBuildScripts);
211                JLabel pathname = new JLabel(scripts.get(i));
212                addItem(pAfterBuildScript, removeAfterBuildScripts, 0, i + 1);
213                addItem(pAfterBuildScript, pathname, 1, i + 1);
214            }
215        }
216    }
217
218    private void updateMoveScriptPanel() {
219        pMoveScript.removeAll();
220        pMoveScript.setLayout(new GridBagLayout());
221        addItem(pMoveScript, addMoveScriptButton, 0, 0);
222
223        // load any existing train move scripts
224        if (_train != null) {
225            List<String> scripts = _train.getMoveScripts();
226            if (scripts.size() > 0) {
227                addItem(pMoveScript, runMoveScriptButton, 1, 0);
228            }
229            for (int i = 0; i < scripts.size(); i++) {
230                JButton removeMoveScripts = new JButton(Bundle.getMessage("RemoveScript"));
231                removeMoveScripts.setName(scripts.get(i));
232                removeMoveScripts.addActionListener(new java.awt.event.ActionListener() {
233                    @Override
234                    public void actionPerformed(java.awt.event.ActionEvent e) {
235                        buttonActionRemoveMoveScript(e);
236                    }
237                });
238                addButtonAction(removeMoveScripts);
239                JLabel pathname = new JLabel(scripts.get(i));
240                addItem(pMoveScript, removeMoveScripts, 0, i + 1);
241                addItem(pMoveScript, pathname, 1, i + 1);
242            }
243        }
244    }
245
246    private void updateTerminationScriptPanel() {
247        pTerminationScript.removeAll();
248        pTerminationScript.setLayout(new GridBagLayout());
249        addItem(pTerminationScript, addTerminationScriptButton, 0, 0);
250
251        // load any existing train termination scripts
252        if (_train != null) {
253            List<String> scripts = _train.getTerminationScripts();
254            if (scripts.size() > 0) {
255                addItem(pTerminationScript, runTerminationScriptButton, 1, 0);
256            }
257            for (int i = 0; i < scripts.size(); i++) {
258                JButton removeTerminationScripts = new JButton(Bundle.getMessage("RemoveScript"));
259                removeTerminationScripts.setName(scripts.get(i));
260                removeTerminationScripts.addActionListener(new java.awt.event.ActionListener() {
261                    @Override
262                    public void actionPerformed(java.awt.event.ActionEvent e) {
263                        buttonActionRemoveTerminationScript(e);
264                    }
265                });
266                JLabel pathname = new JLabel(scripts.get(i));
267                addItem(pTerminationScript, removeTerminationScripts, 0, i + 1);
268                addItem(pTerminationScript, pathname, 1, i + 1);
269            }
270        }
271    }
272
273    // Save train, add scripts buttons
274    @Override
275    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
276        if (_train != null) {
277            if (ae.getSource() == addBuildScriptButton) {
278                log.debug("train add build script button activated");
279                File f = selectFile();
280                if (f != null) {
281                    _train.addBuildScript(FileUtil.getPortableFilename(f));
282                    updateBuildScriptPanel();
283                    pack();
284                }
285            }
286            if (ae.getSource() == addAfterBuildScriptButton) {
287                log.debug("train add after build script button activated");
288                File f = selectFile();
289                if (f != null) {
290                    _train.addAfterBuildScript(FileUtil.getPortableFilename(f));
291                    updateAfterBuildScriptPanel();
292                    pack();
293                }
294            }
295            if (ae.getSource() == addMoveScriptButton) {
296                log.debug("train add move script button activated");
297                File f = selectFile();
298                if (f != null) {
299                    _train.addMoveScript(FileUtil.getPortableFilename(f));
300                    updateMoveScriptPanel();
301                    pack();
302                }
303            }
304            if (ae.getSource() == addTerminationScriptButton) {
305                log.debug("train add termination script button activated");
306                File f = selectFile();
307                if (f != null) {
308                    _train.addTerminationScript(FileUtil.getPortableFilename(f));
309                    updateTerminationScriptPanel();
310                    pack();
311                }
312            }
313            if (ae.getSource() == runBuildScriptButton) {
314                runScripts(_train.getBuildScripts());
315            }
316            if (ae.getSource() == runAfterBuildScriptButton) {
317                runScripts(_train.getAfterBuildScripts());
318            }
319            if (ae.getSource() == runMoveScriptButton) {
320                runScripts(_train.getMoveScripts());
321            }
322            if (ae.getSource() == runTerminationScriptButton) {
323                runScripts(_train.getTerminationScripts());
324            }
325            if (ae.getSource() == saveTrainButton) {
326                log.debug("train save button activated");
327                OperationsXml.save();
328                if (Setup.isCloseWindowOnSaveEnabled()) {
329                    dispose();
330                }
331            }
332        }
333    }
334
335    public void buttonActionRemoveBuildScript(java.awt.event.ActionEvent ae) {
336        if (_train != null) {
337            JButton rbutton = (JButton) ae.getSource();
338            log.debug("remove build script button activated {}", rbutton.getName());
339            _train.deleteBuildScript(rbutton.getName());
340            updateBuildScriptPanel();
341            pack();
342        }
343    }
344
345    public void buttonActionRemoveAfterBuildScript(java.awt.event.ActionEvent ae) {
346        if (_train != null) {
347            JButton rbutton = (JButton) ae.getSource();
348            log.debug("remove after build script button activated {}", rbutton.getName());
349            _train.deleteAfterBuildScript(rbutton.getName());
350            updateAfterBuildScriptPanel();
351            pack();
352        }
353    }
354
355    public void buttonActionRemoveMoveScript(java.awt.event.ActionEvent ae) {
356        if (_train != null) {
357            JButton rbutton = (JButton) ae.getSource();
358            log.debug("remove move script button activated {}", rbutton.getName());
359            _train.deleteMoveScript(rbutton.getName());
360            updateMoveScriptPanel();
361            pack();
362        }
363    }
364
365    public void buttonActionRemoveTerminationScript(java.awt.event.ActionEvent ae) {
366        if (_train != null) {
367            JButton rbutton = (JButton) ae.getSource();
368            log.debug("remove termination script button activated {}", rbutton.getName());
369            _train.deleteTerminationScript(rbutton.getName());
370            updateTerminationScriptPanel();
371            pack();
372        }
373    }
374
375    private void runScripts(List<String> scripts) {
376        for (String script : scripts) {
377            String scriptPathname = jmri.util.FileUtil.getExternalFilename(script);
378            File file = new File(scriptPathname);
379            if (file.exists()) {
380                JmriScriptEngineManager.getDefault().runScript(file);
381            } else {
382                JmriJOptionPane.showMessageDialog(this, script, Bundle.getMessage("ScriptFileNotFound"),
383                        JmriJOptionPane.ERROR_MESSAGE);
384            }
385        }
386    }
387
388    /**
389     * We always use the same file chooser in this class, so that the user's
390     * last-accessed directory remains available.
391     */
392    ScriptFileChooser fc = new ScriptFileChooser(FileUtil.getUserFilesPath());
393
394    private File selectFile() {
395        if (fc == null) {
396            log.error("Could not find user directory");
397        } else {
398            fc.setDialogTitle(Bundle.getMessage("FindDesiredScriptFile"));
399            // when reusing the chooser, make sure new files are included
400            fc.rescanCurrentDirectory();
401            int retVal = fc.showOpenDialog(null);
402            // handle selection or cancel
403            if (retVal == JFileChooser.APPROVE_OPTION) {
404                return fc.getSelectedFile();
405            }
406        }
407        return null;
408    }
409
410    private void enableButtons(boolean enabled) {
411        addBuildScriptButton.setEnabled(enabled);
412        addAfterBuildScriptButton.setEnabled(enabled);
413        addMoveScriptButton.setEnabled(enabled);
414        addTerminationScriptButton.setEnabled(enabled);
415        saveTrainButton.setEnabled(enabled);
416    }
417
418    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrainScriptFrame.class);
419}