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