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