001package jmri.jmrit.dispatcher;
002
003import java.awt.Container;
004import java.awt.FlowLayout;
005import java.awt.event.ActionEvent;
006import java.awt.event.ActionListener;
007import java.awt.event.ItemEvent;
008import java.awt.event.ItemListener;
009import java.util.ArrayList;
010import java.util.HashSet;
011import java.util.List;
012import java.util.Set;
013
014import javax.swing.BorderFactory;
015import javax.swing.BoxLayout;
016import javax.swing.ButtonGroup;
017import javax.swing.JButton;
018import javax.swing.JCheckBox;
019import javax.swing.JComboBox;
020import javax.swing.JLabel;
021import javax.swing.JPanel;
022import javax.swing.JRadioButton;
023import javax.swing.JScrollPane;
024import javax.swing.JSeparator;
025import javax.swing.JSpinner;
026import javax.swing.JTextField;
027import javax.swing.SpinnerNumberModel;
028import javax.swing.event.ChangeEvent;
029import javax.swing.event.ChangeListener;
030
031import jmri.Block;
032import jmri.InstanceManager;
033import jmri.Sensor;
034import jmri.Transit;
035import jmri.TransitManager;
036import jmri.jmrit.dispatcher.ActiveTrain.TrainDetection;
037import jmri.jmrit.dispatcher.ActiveTrain.TrainLengthUnits;
038import jmri.jmrit.dispatcher.DispatcherFrame.TrainsFrom;
039import jmri.jmrit.operations.trains.Train;
040import jmri.jmrit.operations.trains.TrainManager;
041import jmri.jmrit.roster.RosterEntry;
042import jmri.jmrit.roster.swing.RosterEntryComboBox;
043import jmri.swing.NamedBeanComboBox;
044import jmri.util.JmriJFrame;
045import jmri.util.swing.JComboBoxUtil;
046import jmri.util.swing.JmriJOptionPane;
047
048/**
049 * Displays the Activate New Train dialog and processes information entered
050 * there.
051 * <p>
052 * This module works with Dispatcher, which initiates the display of the dialog.
053 * Dispatcher also creates the ActiveTrain.
054 * <p>
055 * This file is part of JMRI.
056 * <p>
057 * JMRI is open source software; you can redistribute it and/or modify it under
058 * the terms of version 2 of the GNU General Public License as published by the
059 * Free Software Foundation. See the "COPYING" file for a copy of this license.
060 * <p>
061 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
062 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
063 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
064 *
065 * @author Dave Duchamp Copyright (C) 2009
066 */
067public class ActivateTrainFrame extends JmriJFrame {
068
069    public ActivateTrainFrame(DispatcherFrame d) {
070        super(true,true);
071        _dispatcher = d;
072        _tiFile = new TrainInfoFile();
073    }
074
075    // operational instance variables
076    private DispatcherFrame _dispatcher = null;
077    private TrainInfoFile _tiFile = null;
078    private final TransitManager _TransitManager = InstanceManager.getDefault(jmri.TransitManager.class);
079    private String _trainInfoName = "";
080
081    // initiate train window variables
082    private Transit selectedTransit = null;
083    //private String selectedTrain = "";
084    private JmriJFrame initiateFrame = null;
085    private Container initiatePane = null;
086    private final jmri.swing.NamedBeanComboBox<Transit> transitSelectBox = new jmri.swing.NamedBeanComboBox<>(_TransitManager);
087    private final JLabel trainBoxLabel = new JLabel("     " + Bundle.getMessage("TrainBoxLabel") + ":");
088    private final JComboBox<Object> trainSelectBox = new JComboBox<>();
089    // private final List<RosterEntry> trainBoxList = new ArrayList<>();
090    private RosterEntryComboBox rosterComboBox = null;
091    private final JLabel trainFieldLabel = new JLabel(Bundle.getMessage("TrainBoxLabel") + ":");
092    private final JTextField trainNameField = new JTextField(10);
093    private final JLabel dccAddressFieldLabel = new JLabel("     " + Bundle.getMessage("DccAddressFieldLabel") + ":");
094    private final JSpinner dccAddressSpinner = new JSpinner(new SpinnerNumberModel(3, 1, 9999, 1));
095    private final JCheckBox inTransitBox = new JCheckBox(Bundle.getMessage("TrainInTransit"));
096    private final JComboBox<String> startingBlockBox = new JComboBox<>();
097    private List<Block> startingBlockBoxList = new ArrayList<>();
098    private List<Integer> startingBlockSeqList = new ArrayList<>();
099    private final JComboBox<String> destinationBlockBox = new JComboBox<>();
100    private List<Block> destinationBlockBoxList = new ArrayList<>();
101    private List<Integer> destinationBlockSeqList = new ArrayList<>();
102    private JButton addNewTrainButton = null;
103    private JButton loadButton = null;
104    private JButton saveButton = null;
105    private JButton saveAsTemplateButton  = null;
106    private JButton deleteButton = null;
107    private final JCheckBox autoRunBox = new JCheckBox(Bundle.getMessage("AutoRun"));
108    private final JCheckBox loadAtStartupBox = new JCheckBox(Bundle.getMessage("LoadAtStartup"));
109
110    private final JRadioButton radioTrainsFromRoster = new JRadioButton(Bundle.getMessage("TrainsFromRoster"));
111    private final JRadioButton radioTrainsFromOps = new JRadioButton(Bundle.getMessage("TrainsFromTrains"));
112    private final JRadioButton radioTrainsFromUser = new JRadioButton(Bundle.getMessage("TrainsFromUser"));
113    private final JRadioButton radioTrainsFromSetLater = new JRadioButton(Bundle.getMessage("TrainsFromSetLater"));
114    private final ButtonGroup trainsFromButtonGroup = new ButtonGroup();
115
116    private final JRadioButton allocateBySafeRadioButton = new JRadioButton(Bundle.getMessage("ToSafeSections"));
117    private final JRadioButton allocateAllTheWayRadioButton = new JRadioButton(Bundle.getMessage("AsFarAsPos"));
118    private final JRadioButton allocateNumberOfBlocks = new JRadioButton(Bundle.getMessage("NumberOfBlocks") + ":");
119    private final ButtonGroup allocateMethodButtonGroup = new ButtonGroup();
120    private final JSpinner allocateCustomSpinner = new JSpinner(new SpinnerNumberModel(3, 1, 100, 1));
121    private final JCheckBox terminateWhenDoneBox = new JCheckBox(Bundle.getMessage("TerminateWhenDone"));
122    private final JPanel terminateWhenDoneDetails = new JPanel();
123    private final JComboBox<String> nextTrain = new JComboBox<>();
124    private final JLabel nextTrainLabel = new JLabel(Bundle.getMessage("TerminateWhenDoneNextTrain"));
125    private final JSpinner prioritySpinner = new JSpinner(new SpinnerNumberModel(5, 0, 100, 1));
126    private final JCheckBox resetWhenDoneBox = new JCheckBox(Bundle.getMessage("ResetWhenDone"));
127    private final JCheckBox reverseAtEndBox = new JCheckBox(Bundle.getMessage("ReverseAtEnd"));
128
129    int delayedStartInt[] = new int[]{ActiveTrain.NODELAY, ActiveTrain.TIMEDDELAY, ActiveTrain.SENSORDELAY};
130    String delayedStartString[] = new String[]{Bundle.getMessage("DelayedStartNone"), Bundle.getMessage("DelayedStartTimed"), Bundle.getMessage("DelayedStartSensor")};
131
132    private final JComboBox<String> reverseDelayedRestartType = new JComboBox<>(delayedStartString);
133    private final JLabel delayReverseReStartLabel = new JLabel(Bundle.getMessage("DelayRestart"));
134    private final JLabel delayReverseReStartSensorLabel = new JLabel(Bundle.getMessage("RestartSensor"));
135    private final JCheckBox delayReverseResetSensorBox = new JCheckBox(Bundle.getMessage("ResetRestartSensor"));
136    private final NamedBeanComboBox<Sensor> delayReverseReStartSensor = new NamedBeanComboBox<>(jmri.InstanceManager.sensorManagerInstance());
137    private final JSpinner delayReverseMinSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));
138    private final JLabel delayReverseMinLabel = new JLabel(Bundle.getMessage("RestartTimed"));
139
140    private final JCheckBox resetStartSensorBox = new JCheckBox(Bundle.getMessage("ResetStartSensor"));
141    private final JComboBox<String> delayedStartBox = new JComboBox<>(delayedStartString);
142    private final JLabel delayedReStartLabel = new JLabel(Bundle.getMessage("DelayRestart"));
143    private final JLabel delayReStartSensorLabel = new JLabel(Bundle.getMessage("RestartSensor"));
144    private final JCheckBox resetRestartSensorBox = new JCheckBox(Bundle.getMessage("ResetRestartSensor"));
145    private final JComboBox<String> delayedReStartBox = new JComboBox<>(delayedStartString);
146    private final NamedBeanComboBox<Sensor> delaySensor = new NamedBeanComboBox<>(jmri.InstanceManager.sensorManagerInstance());
147    private final NamedBeanComboBox<Sensor> delayReStartSensor = new NamedBeanComboBox<>(jmri.InstanceManager.sensorManagerInstance());
148
149    private final JSpinner departureHrSpinner = new JSpinner(new SpinnerNumberModel(8, 0, 23, 1));
150    private final JSpinner departureMinSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 59, 1));
151    private final JLabel departureTimeLabel = new JLabel(Bundle.getMessage("DepartureTime"));
152    private final JLabel departureSepLabel = new JLabel(":");
153
154    private final JSpinner delayMinSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));
155    private final JLabel delayMinLabel = new JLabel(Bundle.getMessage("RestartTimed"));
156
157    private final JComboBox<String> trainTypeBox = new JComboBox<>();
158    // Note: See also items related to automatically running trains near the end of this module
159
160    boolean transitsFromSpecificBlock = false;
161
162    private TrainInfo trainInfo;
163
164    private final String nameOfTemplateFile="TrainInfoDefaultTemplate.xml";
165
166    /**
167     * Open up a new train window for a given roster entry located in a specific
168     * block.
169     *
170     * @param e  the action event triggering the new window
171     * @param re the roster entry to open the new window for
172     * @param b  the block where the train is located
173     */
174    public void initiateTrain(ActionEvent e, RosterEntry re, Block b) {
175        initiateTrain(e);
176        if (trainInfo.getTrainsFrom() == TrainsFrom.TRAINSFROMROSTER && re != null) {
177            setRosterComboBox(rosterComboBox, re.getId());
178            //Add in some bits of code as some point to filter down the transits that can be used.
179        }
180        if (b != null && selectedTransit != null) {
181            List<Transit> transitList = _TransitManager.getListUsingBlock(b);
182            List<Transit> transitEntryList = _TransitManager.getListEntryBlock(b);
183            for (Transit t : transitEntryList) {
184                if (!transitList.contains(t)) {
185                    transitList.add(t);
186                }
187            }
188            transitsFromSpecificBlock = true;
189            initializeFreeTransitsCombo(transitList);
190            List<Block> tmpBlkList = new ArrayList<>();
191            if (selectedTransit.getEntryBlocksList().contains(b)) {
192                tmpBlkList = selectedTransit.getEntryBlocksList();
193                inTransitBox.setSelected(false);
194            } else if (selectedTransit.containsBlock(b)) {
195                tmpBlkList = selectedTransit.getInternalBlocksList();
196                inTransitBox.setSelected(true);
197            }
198            List<Integer> tmpSeqList = selectedTransit.getBlockSeqList();
199            for (int i = 0; i < tmpBlkList.size(); i++) {
200                if (tmpBlkList.get(i) == b) {
201                    setComboBox(startingBlockBox, getBlockName(b) + "-" + tmpSeqList.get(i));
202                    break;
203                }
204            }
205        }
206    }
207
208    /**
209     * Displays a window that allows a new ActiveTrain to be activated.
210     * <p>
211     * Called by Dispatcher in response to the dispatcher clicking the New Train
212     * button.
213     *
214     * @param e the action event triggering the window display
215     */
216    protected void initiateTrain(ActionEvent e) {
217        // set Dispatcher defaults
218        // create window if needed
219        // if template exists open it
220        try {
221            trainInfo = _tiFile.readTrainInfo(nameOfTemplateFile);
222            if (trainInfo == null) {
223                trainInfo = new TrainInfo();
224            }
225        } catch (java.io.IOException ioe) {
226            log.error("IO Exception when reading train info file", ioe);
227            return;
228        } catch (org.jdom2.JDOMException jde) {
229            log.error("JDOM Exception when reading train info file", jde);
230            return;
231        }
232        trainInfo.setTrainsFrom(_dispatcher.getTrainsFrom());
233
234        if (initiateFrame == null) {
235            initiateFrame = this;
236            initiateFrame.setTitle(Bundle.getMessage("AddTrainTitle"));
237            initiateFrame.addHelpMenu("package.jmri.jmrit.dispatcher.NewTrain", true);
238            initiatePane = initiateFrame.getContentPane();
239            initiatePane.setLayout(new BoxLayout(initiatePane, BoxLayout.Y_AXIS));
240
241            // add buttons to load and save train information
242            JPanel p0 = new JPanel();
243            p0.setLayout(new FlowLayout());
244            p0.add(loadButton = new JButton(Bundle.getMessage("LoadButton")));
245            loadButton.addActionListener(new ActionListener() {
246                @Override
247                public void actionPerformed(ActionEvent e) {
248                    loadTrainInfo(e);
249                }
250            });
251            loadButton.setToolTipText(Bundle.getMessage("LoadButtonHint"));
252            p0.add(saveButton = new JButton(Bundle.getMessage("SaveButton")));
253            saveButton.addActionListener(new ActionListener() {
254                @Override
255                public void actionPerformed(ActionEvent e) {
256                    saveTrainInfo(e);
257                }
258            });
259            saveButton.setToolTipText(Bundle.getMessage("SaveButtonHint"));
260            p0.add(saveAsTemplateButton = new JButton(Bundle.getMessage("SaveAsTemplateButton")));
261            saveAsTemplateButton.addActionListener(new ActionListener() {
262                @Override
263                public void actionPerformed(ActionEvent e) {
264                    saveTrainInfoAsTemplate(e);
265                }
266            });
267            saveAsTemplateButton.setToolTipText(Bundle.getMessage("SaveAsTemplateButtonHint"));
268            p0.add(deleteButton = new JButton(Bundle.getMessage("DeleteButton")));
269            deleteButton.addActionListener(new ActionListener() {
270                @Override
271                public void actionPerformed(ActionEvent e) {
272                    deleteTrainInfo(e);
273                }
274            });
275            deleteButton.setToolTipText(Bundle.getMessage("DeleteButtonHint"));
276
277            // add items relating to both manually run and automatic trains.
278
279            // Trains From choices.
280            JPanel p0a = new JPanel();
281            p0a.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainsFrom")));
282            p0a.setLayout(new FlowLayout());
283            radioTrainsFromRoster.setActionCommand("TRAINSFROMROSTER");
284            trainsFromButtonGroup.add(radioTrainsFromRoster);
285            radioTrainsFromOps.setActionCommand("TRAINSFROMOPS");
286            trainsFromButtonGroup.add(radioTrainsFromOps);
287            radioTrainsFromUser.setActionCommand("TRAINSFROMUSER");
288            trainsFromButtonGroup.add(radioTrainsFromUser);
289            radioTrainsFromSetLater.setActionCommand("TRAINSFROMSETLATER");
290            trainsFromButtonGroup.add(radioTrainsFromSetLater);
291            p0a.add(radioTrainsFromRoster);
292            radioTrainsFromRoster.setToolTipText(Bundle.getMessage("TrainsFromRosterHint"));
293            p0a.add(radioTrainsFromOps);
294            radioTrainsFromOps.setToolTipText(Bundle.getMessage("TrainsFromTrainsHint"));
295            p0a.add(radioTrainsFromUser);
296            radioTrainsFromUser.setToolTipText(Bundle.getMessage("TrainsFromUserHint"));
297            p0a.add(radioTrainsFromSetLater);
298            radioTrainsFromSetLater.setToolTipText(Bundle.getMessage("TrainsFromSetLaterHint"));
299
300            radioTrainsFromOps.addItemListener(new ItemListener() {
301                @Override
302                public void itemStateChanged(ItemEvent e)  {
303                    if (e.getStateChange() == ItemEvent.SELECTED) {
304                        trainInfo.setTrainsFrom(TrainsFrom.TRAINSFROMOPS);
305                        setTrainsFromOptions(trainInfo.getTrainsFrom());
306                    }
307                }
308            });
309
310            radioTrainsFromRoster.addItemListener(new ItemListener() {
311                @Override
312                public void itemStateChanged(ItemEvent e)  {
313                    if (e.getStateChange() == ItemEvent.SELECTED) {
314                        trainInfo.setTrainsFrom(TrainsFrom.TRAINSFROMROSTER);
315                         setTrainsFromOptions(trainInfo.getTrainsFrom());
316                    }
317                }
318            });
319            radioTrainsFromUser.addItemListener(new ItemListener() {
320                @Override
321                public void itemStateChanged(ItemEvent e)  {
322                    if (e.getStateChange() == ItemEvent.SELECTED) {
323                        trainInfo.setTrainsFrom(TrainsFrom.TRAINSFROMUSER);
324                        setTrainsFromOptions(trainInfo.getTrainsFrom());
325                    }
326                }
327            });
328            radioTrainsFromSetLater.addItemListener(new ItemListener() {
329                @Override
330                public void itemStateChanged(ItemEvent e)  {
331                    if (e.getStateChange() == ItemEvent.SELECTED) {
332                        trainInfo.setTrainsFrom(TrainsFrom.TRAINSFROMSETLATER);
333                        setTrainsFromOptions(trainInfo.getTrainsFrom());
334                    }
335                }
336            });
337            p0a.add(allocateCustomSpinner);
338            initiatePane.add(p0a);
339
340            JPanel p1 = new JPanel();
341            p1.setLayout(new FlowLayout());
342            p1.add(new JLabel(Bundle.getMessage("TransitBoxLabel") + " :"));
343            p1.add(transitSelectBox);
344            transitSelectBox.addActionListener(new ActionListener() {
345                @Override
346                public void actionPerformed(ActionEvent e) {
347                    handleTransitSelectionChanged(e);
348                }
349            });
350            transitSelectBox.setToolTipText(Bundle.getMessage("TransitBoxHint"));
351            p1.add(trainBoxLabel);
352            p1.add(trainSelectBox);
353            trainSelectBox.addActionListener(new ActionListener() {
354                @Override
355                public void actionPerformed(ActionEvent e)  {
356                        handleTrainSelectionChanged();
357                }
358            });
359            trainSelectBox.setToolTipText(Bundle.getMessage("TrainBoxHint"));
360
361            rosterComboBox = new RosterEntryComboBox();
362            initializeFreeRosterEntriesCombo();
363            rosterComboBox.addActionListener(new ActionListener() {
364                @Override
365                public void actionPerformed(ActionEvent e) {
366                    handleRosterSelectionChanged(e);
367                }
368            });
369            p1.add(rosterComboBox);
370
371            initiatePane.add(p1);
372            JPanel p1a = new JPanel();
373            p1a.setLayout(new FlowLayout());
374            p1a.add(trainFieldLabel);
375            p1a.add(trainNameField);
376            trainNameField.setToolTipText(Bundle.getMessage("TrainFieldHint"));
377            p1a.add(dccAddressFieldLabel);
378            p1a.add(dccAddressSpinner);
379            dccAddressSpinner.setToolTipText(Bundle.getMessage("DccAddressFieldHint"));
380            initiatePane.add(p1a);
381            JPanel p2 = new JPanel();
382            p2.setLayout(new FlowLayout());
383            p2.add(inTransitBox);
384            inTransitBox.addActionListener(new ActionListener() {
385                @Override
386                public void actionPerformed(ActionEvent e) {
387                    handleInTransitClick(e);
388                }
389            });
390            inTransitBox.setToolTipText(Bundle.getMessage("InTransitBoxHint"));
391            initiatePane.add(p2);
392            JPanel p3 = new JPanel();
393            p3.setLayout(new FlowLayout());
394            p3.add(new JLabel(Bundle.getMessage("StartingBlockBoxLabel") + " :"));
395            p3.add(startingBlockBox);
396            startingBlockBox.setToolTipText(Bundle.getMessage("StartingBlockBoxHint"));
397            startingBlockBox.addActionListener(new ActionListener() {
398                @Override
399                public void actionPerformed(ActionEvent e) {
400                    handleStartingBlockSelectionChanged(e);
401                }
402            });
403            initiatePane.add(p3);
404            JPanel p4 = new JPanel();
405            p4.setLayout(new FlowLayout());
406            p4.add(new JLabel(Bundle.getMessage("DestinationBlockBoxLabel") + ":"));
407            p4.add(destinationBlockBox);
408            destinationBlockBox.setToolTipText(Bundle.getMessage("DestinationBlockBoxHint"));
409            JPanel p4a = new JPanel();
410            initiatePane.add(p4);
411            p4a.add(trainDetectionLabel);
412            initializeTrainDetectionBox();
413            p4a.add(trainDetectionComboBox);
414            trainDetectionComboBox.setToolTipText(Bundle.getMessage("TrainDetectionBoxHint"));
415            initiatePane.add(p4a);
416            JPanel p4b = new JPanel();
417            p4b.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("AllocateMethodLabel")));
418            p4b.setLayout(new FlowLayout());
419            allocateMethodButtonGroup.add(allocateAllTheWayRadioButton);
420            allocateMethodButtonGroup.add(allocateBySafeRadioButton);
421            allocateMethodButtonGroup.add(allocateNumberOfBlocks);
422            p4b.add(allocateAllTheWayRadioButton);
423            allocateAllTheWayRadioButton.setToolTipText(Bundle.getMessage("AllocateAllTheWayHint"));
424            p4b.add(allocateBySafeRadioButton);
425            allocateBySafeRadioButton.setToolTipText(Bundle.getMessage("AllocateSafeHint"));
426            p4b.add(allocateNumberOfBlocks);
427            allocateNumberOfBlocks.setToolTipText(Bundle.getMessage("AllocateMethodHint"));
428            allocateAllTheWayRadioButton.addActionListener(new ActionListener() {
429                @Override
430                public void actionPerformed(ActionEvent e) {
431                    handleAllocateAllTheWayButtonChanged(e);
432                }
433            });
434            allocateBySafeRadioButton.addActionListener(new ActionListener() {
435                @Override
436                public void actionPerformed(ActionEvent e) {
437                    handleAllocateBySafeButtonChanged(e);
438                }
439            });
440            allocateNumberOfBlocks.addActionListener(new ActionListener() {
441                @Override
442                public void actionPerformed(ActionEvent e) {
443                    handleAllocateNumberOfBlocksButtonChanged(e);
444                }
445            });
446            p4b.add(allocateCustomSpinner);
447            allocateCustomSpinner.setToolTipText(Bundle.getMessage("AllocateMethodHint"));
448            initiatePane.add(p4b);
449            JPanel p6 = new JPanel();
450            p6.setLayout(new FlowLayout());
451            p6.add(resetWhenDoneBox);
452            resetWhenDoneBox.addActionListener(new ActionListener() {
453                @Override
454                public void actionPerformed(ActionEvent e) {
455                    handleResetWhenDoneClick(e);
456                }
457            });
458            resetWhenDoneBox.setToolTipText(Bundle.getMessage("ResetWhenDoneBoxHint"));
459            initiatePane.add(p6);
460            JPanel p6a = new JPanel();
461            p6a.setLayout(new FlowLayout());
462            ((FlowLayout) p6a.getLayout()).setVgap(1);
463            p6a.add(delayedReStartLabel);
464            p6a.add(delayedReStartBox);
465            p6a.add(resetRestartSensorBox);
466            resetRestartSensorBox.setToolTipText(Bundle.getMessage("ResetRestartSensorHint"));
467            resetRestartSensorBox.setSelected(true);
468            delayedReStartBox.addActionListener(new ActionListener() {
469                @Override
470                public void actionPerformed(ActionEvent e) {
471                    handleResetWhenDoneClick(e);
472                }
473            });
474            delayedReStartBox.setToolTipText(Bundle.getMessage("DelayedReStartHint"));
475            initiatePane.add(p6a);
476
477            JPanel p6b = new JPanel();
478            p6b.setLayout(new FlowLayout());
479            ((FlowLayout) p6b.getLayout()).setVgap(1);
480            p6b.add(delayMinLabel);
481            p6b.add(delayMinSpinner); // already set to 0
482            delayMinSpinner.setToolTipText(Bundle.getMessage("RestartTimedHint"));
483            p6b.add(delayReStartSensorLabel);
484            p6b.add(delayReStartSensor);
485            delayReStartSensor.setAllowNull(true);
486            handleResetWhenDoneClick(null);
487            initiatePane.add(p6b);
488
489            initiatePane.add(new JSeparator());
490
491            JPanel p10 = new JPanel();
492            p10.setLayout(new FlowLayout());
493            p10.add(reverseAtEndBox);
494            reverseAtEndBox.setToolTipText(Bundle.getMessage("ReverseAtEndBoxHint"));
495            initiatePane.add(p10);
496            reverseAtEndBox.addActionListener(new ActionListener() {
497                @Override
498                public void actionPerformed(ActionEvent e) {
499                    handleReverseAtEndBoxClick(e);
500                }
501            });
502            JPanel pDelayReverseRestartDetails = new JPanel();
503            pDelayReverseRestartDetails.setLayout(new FlowLayout());
504            ((FlowLayout) pDelayReverseRestartDetails.getLayout()).setVgap(1);
505            pDelayReverseRestartDetails.add(delayReverseReStartLabel);
506            pDelayReverseRestartDetails.add(reverseDelayedRestartType);
507            pDelayReverseRestartDetails.add(delayReverseResetSensorBox);
508            delayReverseResetSensorBox.setToolTipText(Bundle.getMessage("ReverseResetRestartSensorHint"));
509            delayReverseResetSensorBox.setSelected(true);
510            reverseDelayedRestartType.addActionListener(new ActionListener() {
511                @Override
512                public void actionPerformed(ActionEvent e) {
513                    handleReverseAtEndBoxClick(e);
514                }
515            });
516            reverseDelayedRestartType.setToolTipText(Bundle.getMessage("ReverseDelayedReStartHint"));
517            initiatePane.add(pDelayReverseRestartDetails);
518
519            JPanel pDelayReverseRestartDetails2 = new JPanel();
520            pDelayReverseRestartDetails2.setLayout(new FlowLayout());
521            ((FlowLayout) pDelayReverseRestartDetails2.getLayout()).setVgap(1);
522            pDelayReverseRestartDetails2.add(delayReverseMinLabel);
523            pDelayReverseRestartDetails2.add(delayReverseMinSpinner); // already set to 0
524            delayReverseMinSpinner.setToolTipText(Bundle.getMessage("ReverseRestartTimedHint"));
525            pDelayReverseRestartDetails2.add(delayReverseReStartSensorLabel);
526            pDelayReverseRestartDetails2.add(delayReverseReStartSensor);
527            delayReverseReStartSensor.setAllowNull(true);
528            handleReverseAtEndBoxClick(null);
529            initiatePane.add(pDelayReverseRestartDetails2);
530
531            initiatePane.add(new JSeparator());
532
533            JPanel p10a = new JPanel();
534            p10a.setLayout(new FlowLayout());
535            p10a.add(terminateWhenDoneBox);
536            terminateWhenDoneBox.addActionListener(new ActionListener() {
537                @Override
538                public void actionPerformed(ActionEvent e) {
539                    handleTerminateWhenDoneBoxClick(e);
540                }
541            });
542            initiatePane.add(p10a);
543            terminateWhenDoneDetails.setLayout(new FlowLayout());
544            terminateWhenDoneDetails.add(nextTrainLabel);
545            terminateWhenDoneDetails.add(nextTrain);
546            nextTrain.setToolTipText(Bundle.getMessage("TerminateWhenDoneNextTrainHint"));
547            initiatePane.add(terminateWhenDoneDetails);
548            handleTerminateWhenDoneBoxClick(null);
549
550            initiatePane.add(new JSeparator());
551
552            JPanel p8 = new JPanel();
553            p8.setLayout(new FlowLayout());
554            p8.add(new JLabel(Bundle.getMessage("PriorityLabel") + ":"));
555            p8.add(prioritySpinner); // already set to 5
556            prioritySpinner.setToolTipText(Bundle.getMessage("PriorityHint"));
557            p8.add(new JLabel("     "));
558            p8.add(new JLabel(Bundle.getMessage("TrainTypeBoxLabel")));
559            initializeTrainTypeBox();
560            p8.add(trainTypeBox);
561            trainTypeBox.setSelectedIndex(1);
562            trainTypeBox.setToolTipText(Bundle.getMessage("TrainTypeBoxHint"));
563            initiatePane.add(p8);
564            JPanel p9 = new JPanel();
565            p9.setLayout(new FlowLayout());
566            p9.add(new JLabel(Bundle.getMessage("DelayedStart")));
567            p9.add(delayedStartBox);
568            delayedStartBox.setToolTipText(Bundle.getMessage("DelayedStartHint"));
569            delayedStartBox.addActionListener(new ActionListener() {
570                @Override
571                public void actionPerformed(ActionEvent e) {
572                    handleDelayStartClick(e);
573                }
574            });
575            p9.add(departureTimeLabel);
576            departureHrSpinner.setEditor(new JSpinner.NumberEditor(departureHrSpinner, "00"));
577            p9.add(departureHrSpinner);
578            departureHrSpinner.setValue(8);
579            departureHrSpinner.setToolTipText(Bundle.getMessage("DepartureTimeHrHint"));
580            p9.add(departureSepLabel);
581            departureMinSpinner.setEditor(new JSpinner.NumberEditor(departureMinSpinner, "00"));
582            p9.add(departureMinSpinner);
583            departureMinSpinner.setValue(0);
584            departureMinSpinner.setToolTipText(Bundle.getMessage("DepartureTimeMinHint"));
585            p9.add(delaySensor);
586            delaySensor.setAllowNull(true);
587            p9.add(resetStartSensorBox);
588            resetStartSensorBox.setToolTipText(Bundle.getMessage("ResetStartSensorHint"));
589            resetStartSensorBox.setSelected(true);
590            handleDelayStartClick(null);
591            initiatePane.add(p9);
592
593            JPanel p11 = new JPanel();
594            p11.setLayout(new FlowLayout());
595            p11.add(loadAtStartupBox);
596            loadAtStartupBox.setToolTipText(Bundle.getMessage("LoadAtStartupBoxHint"));
597            loadAtStartupBox.setSelected(false);
598            initiatePane.add(p11);
599
600            initiatePane.add(new JSeparator());
601            JPanel p5 = new JPanel();
602            p5.setLayout(new FlowLayout());
603            p5.add(autoRunBox);
604            autoRunBox.addActionListener(new ActionListener() {
605                @Override
606                public void actionPerformed(ActionEvent e) {
607                    handleAutoRunClick(e);
608                }
609            });
610            autoRunBox.setToolTipText(Bundle.getMessage("AutoRunBoxHint"));
611            autoRunBox.setSelected(false);
612            initiatePane.add(p5);
613
614            initializeAutoRunItems();
615
616            JPanel p7 = new JPanel();
617            p7.setLayout(new FlowLayout());
618            JButton cancelButton = null;
619            p7.add(cancelButton = new JButton(Bundle.getMessage("ButtonCancel")));
620            cancelButton.addActionListener(new ActionListener() {
621                @Override
622                public void actionPerformed(ActionEvent e) {
623                    cancelInitiateTrain(e);
624                }
625            });
626            cancelButton.setToolTipText(Bundle.getMessage("CancelButtonHint"));
627            p7.add(addNewTrainButton = new JButton(Bundle.getMessage("ButtonCreate")));
628            addNewTrainButton.addActionListener(new ActionListener() {
629                @Override
630                public void actionPerformed(ActionEvent e) {
631                    addNewTrain(e);
632                }
633            });
634            addNewTrainButton.setToolTipText(Bundle.getMessage("AddNewTrainButtonHint"));
635
636            JPanel mainPane = new JPanel();
637            mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
638            JScrollPane scrPane = new JScrollPane(initiatePane);
639            mainPane.add(p0);
640            mainPane.add(scrPane);
641            mainPane.add(p7);
642            initiateFrame.setContentPane(mainPane);
643            switch (trainInfo.getTrainsFrom()) {
644                case TRAINSFROMROSTER:
645                    radioTrainsFromRoster.setSelected(true);
646                    break;
647                case TRAINSFROMOPS:
648                    radioTrainsFromOps.setSelected(true);
649                    break;
650                case TRAINSFROMUSER:
651                     radioTrainsFromUser.setSelected(true);
652                     break;
653                case TRAINSFROMSETLATER:
654                default:
655                    radioTrainsFromSetLater.setSelected(true);
656            }
657
658        }
659        autoRunBox.setSelected(false);
660        loadAtStartupBox.setSelected(false);
661        initializeFreeTransitsCombo(new ArrayList<Transit>());
662        nextTrain.addItem("");
663        refreshNextTrainCombo();
664        setTrainsFromOptions(trainInfo.getTrainsFrom());
665        initiateFrame.pack();
666        initiateFrame.setVisible(true);
667
668        trainInfoToDialog(trainInfo);
669    }
670
671    private void refreshNextTrainCombo() {
672        Object saveEntry = null;
673        if (nextTrain.getSelectedIndex() > 0) {
674            saveEntry=nextTrain.getSelectedItem();
675        }
676        nextTrain.removeAll();
677        for (String file: _tiFile.getTrainInfoFileNames()) {
678            nextTrain.addItem(file);
679        }
680        if (saveEntry != null) {
681            nextTrain.setSelectedItem(saveEntry);
682        }
683    }
684    private void setTrainsFromOptions(TrainsFrom transFrom) {
685        switch (transFrom) {
686            case TRAINSFROMROSTER:
687                initializeFreeRosterEntriesCombo();
688                trainBoxLabel.setVisible(true);
689                rosterComboBox.setVisible(true);
690                trainSelectBox.setVisible(false);
691                trainFieldLabel.setVisible(true);
692                trainNameField.setVisible(true);
693                dccAddressFieldLabel.setVisible(false);
694                dccAddressSpinner.setVisible(false);
695                break;
696            case TRAINSFROMOPS:
697                initializeFreeTrainsCombo();
698                trainBoxLabel.setVisible(true);
699                trainSelectBox.setVisible(true);
700                rosterComboBox.setVisible(false);
701                trainFieldLabel.setVisible(true);
702                trainNameField.setVisible(true);
703                dccAddressFieldLabel.setVisible(true);
704                dccAddressSpinner.setVisible(true);
705                setSpeedProfileOptions(trainInfo,false);
706                break;
707            case TRAINSFROMUSER:
708                trainNameField.setText("");
709                trainBoxLabel.setVisible(false);
710                trainSelectBox.setVisible(false);
711                rosterComboBox.setVisible(false);
712                trainFieldLabel.setVisible(true);
713                trainNameField.setVisible(true);
714                dccAddressFieldLabel.setVisible(true);
715                dccAddressSpinner.setVisible(true);
716                dccAddressSpinner.setEnabled(true);
717                setSpeedProfileOptions(trainInfo,false);
718                break;
719            case TRAINSFROMSETLATER:
720            default:
721                trainBoxLabel.setVisible(false);
722                rosterComboBox.setVisible(false);
723                trainSelectBox.setVisible(false);
724                trainFieldLabel.setVisible(true);
725                trainNameField.setVisible(true);
726                dccAddressFieldLabel.setVisible(false);
727                dccAddressSpinner.setVisible(false);
728        }
729    }
730
731    private void initializeTrainTypeBox() {
732        trainTypeBox.removeAllItems();
733        trainTypeBox.addItem("<" + Bundle.getMessage("None").toLowerCase() + ">"); // <none>
734        trainTypeBox.addItem(Bundle.getMessage("LOCAL_PASSENGER"));
735        trainTypeBox.addItem(Bundle.getMessage("LOCAL_FREIGHT"));
736        trainTypeBox.addItem(Bundle.getMessage("THROUGH_PASSENGER"));
737        trainTypeBox.addItem(Bundle.getMessage("THROUGH_FREIGHT"));
738        trainTypeBox.addItem(Bundle.getMessage("EXPRESS_PASSENGER"));
739        trainTypeBox.addItem(Bundle.getMessage("EXPRESS_FREIGHT"));
740        trainTypeBox.addItem(Bundle.getMessage("MOW"));
741        // NOTE: The above must correspond in order and name to definitions in ActiveTrain.java.
742    }
743
744    private void initializeTrainDetectionBox() {
745        trainDetectionComboBox.addItem(new TrainDetectionItem(Bundle.getMessage("TrainDetectionWholeTrain"),TrainDetection.TRAINDETECTION_WHOLETRAIN));
746        trainDetectionComboBox.addItem(new TrainDetectionItem(Bundle.getMessage("TrainDetectionHeadAndTail"),TrainDetection.TRAINDETECTION_HEADANDTAIL));
747        trainDetectionComboBox.addItem(new TrainDetectionItem(Bundle.getMessage("TrainDetectionHeadOnly"),TrainDetection.TRAINDETECTION_HEADONLY));
748    }
749
750    private void initializeScaleLengthBox() {
751        trainLengthUnitsComboBox.addItem(new TrainLengthUnitsItem(Bundle.getMessage("TrainLengthInScaleFeet"), TrainLengthUnits.TRAINLENGTH_SCALEFEET));
752        trainLengthUnitsComboBox.addItem(new TrainLengthUnitsItem(Bundle.getMessage("TrainLengthInScaleMeters"), TrainLengthUnits.TRAINLENGTH_SCALEMETERS));
753        trainLengthUnitsComboBox.addItem(new TrainLengthUnitsItem(Bundle.getMessage("TrainLengthInActualInchs"), TrainLengthUnits.TRAINLENGTH_ACTUALINCHS));
754        trainLengthUnitsComboBox.addItem(new TrainLengthUnitsItem(Bundle.getMessage("TrainLengthInActualcm"), TrainLengthUnits.TRAINLENGTH_ACTUALCM));
755    }
756
757    private void handleTransitSelectionChanged(ActionEvent e) {
758        int index = transitSelectBox.getSelectedIndex();
759        if (index < 0) {
760            return;
761        }
762        Transit t = transitSelectBox.getSelectedItem();
763        if ((t != null) && (t != selectedTransit)) {
764            selectedTransit = t;
765            initializeStartingBlockCombo();
766            initializeDestinationBlockCombo();
767            initiateFrame.pack();
768        }
769    }
770
771    private void handleInTransitClick(ActionEvent e) {
772        if (!inTransitBox.isSelected() && selectedTransit.getEntryBlocksList().isEmpty()) {
773            JmriJOptionPane.showMessageDialog(initiateFrame, Bundle
774                    .getMessage("NoEntryBlocks"), Bundle.getMessage("MessageTitle"),
775                    JmriJOptionPane.INFORMATION_MESSAGE);
776            inTransitBox.setSelected(true);
777        }
778        initializeStartingBlockCombo();
779        initializeDestinationBlockCombo();
780        initiateFrame.pack();
781    }
782
783 //   private void handleTrainSelectionChanged(ActionEvent e) {
784      private void handleTrainSelectionChanged() {
785        if (trainInfo.getTrainsFrom() != TrainsFrom.TRAINSFROMOPS) {
786            return;
787        }
788        int ix = trainSelectBox.getSelectedIndex();
789        if (ix < 1) { // no train selected
790            dccAddressSpinner.setEnabled(false);
791            return;
792        }
793        dccAddressSpinner.setEnabled(true);
794        int dccAddress;
795        try {
796            dccAddress = Integer.parseInt((((Train) trainSelectBox.getSelectedItem()).getLeadEngineDccAddress()));
797        } catch (NumberFormatException Ex) {
798            JmriJOptionPane.showMessageDialog(initiateFrame, Bundle.getMessage("Error43"),
799                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
800            return;
801        }
802        dccAddressSpinner.setValue (dccAddress);
803    }
804
805    private void handleRosterSelectionChanged(ActionEvent e) {
806        if (trainInfo.getTrainsFrom() != TrainsFrom.TRAINSFROMROSTER) {
807            return;
808        }
809        int ix = rosterComboBox.getSelectedIndex();
810        if (ix > 0) { // first item is "Select Loco" string
811            RosterEntry r = (RosterEntry) rosterComboBox.getItemAt(ix);
812            // check to see if speed profile exists and is not empty
813            if (r.getSpeedProfile() == null || r.getSpeedProfile().getProfileSize() < 1) {
814                // disable profile boxes etc.
815                setSpeedProfileOptions(trainInfo,false);
816            } else {
817                // enable profile boxes
818                setSpeedProfileOptions(trainInfo,true);
819            }
820            trainInfo.setMaxSpeed(r.getMaxSpeedPCT()/100.0f);
821            maxSpeedSpinner.setValue(trainInfo.getMaxSpeed());
822            trainNameField.setText(r.titleString());
823            if (r.getAttribute("DispatcherTrainType") != null && !r.getAttribute("DispatcherTrainType").equals("")) {
824                trainTypeBox.setSelectedItem(r.getAttribute("DispatcherTrainType"));
825            }
826        } else {
827            setSpeedProfileOptions(trainInfo,false);
828        }
829    }
830
831    private void handleDelayStartClick(ActionEvent e) {
832        departureHrSpinner.setVisible(false);
833        departureMinSpinner.setVisible(false);
834        departureTimeLabel.setVisible(false);
835        departureSepLabel.setVisible(false);
836        delaySensor.setVisible(false);
837        resetStartSensorBox.setVisible(false);
838        if (delayedStartBox.getSelectedItem().equals(Bundle.getMessage("DelayedStartTimed"))) {
839            departureHrSpinner.setVisible(true);
840            departureMinSpinner.setVisible(true);
841            departureTimeLabel.setVisible(true);
842            departureSepLabel.setVisible(true);
843        } else if (delayedStartBox.getSelectedItem().equals(Bundle.getMessage("DelayedStartSensor"))) {
844            delaySensor.setVisible(true);
845            resetStartSensorBox.setVisible(true);
846        }
847        initiateFrame.pack(); // to fit extra hh:mm in window
848    }
849
850    private void handleResetWhenDoneClick(ActionEvent e) {
851        delayMinSpinner.setVisible(false);
852        delayMinLabel.setVisible(false);
853        delayedReStartLabel.setVisible(false);
854        delayedReStartBox.setVisible(false);
855        delayReStartSensorLabel.setVisible(false);
856        delayReStartSensor.setVisible(false);
857        resetRestartSensorBox.setVisible(false);
858        if (resetWhenDoneBox.isSelected()) {
859            delayedReStartLabel.setVisible(true);
860            delayedReStartBox.setVisible(true);
861            terminateWhenDoneBox.setSelected(false);
862            if (delayedReStartBox.getSelectedItem().equals(Bundle.getMessage("DelayedStartTimed"))) {
863                delayMinSpinner.setVisible(true);
864                delayMinLabel.setVisible(true);
865            } else if (delayedReStartBox.getSelectedItem().equals(Bundle.getMessage("DelayedStartSensor"))) {
866                delayReStartSensor.setVisible(true);
867                delayReStartSensorLabel.setVisible(true);
868                resetRestartSensorBox.setVisible(true);
869            }
870        } else {
871            terminateWhenDoneBox.setEnabled(true);
872        }
873        initiateFrame.pack();
874    }
875
876    private void handleTerminateWhenDoneBoxClick(ActionEvent e) {
877        if (terminateWhenDoneBox.isSelected()) {
878            refreshNextTrainCombo();
879            resetWhenDoneBox.setSelected(false);
880            terminateWhenDoneDetails.setVisible(true);
881        } else {
882            terminateWhenDoneDetails.setVisible(false);
883            // leave it
884            //nextTrain.setSelectedItem("");
885        }
886    }
887    private void handleReverseAtEndBoxClick(ActionEvent e) {
888        delayReverseMinSpinner.setVisible(false);
889        delayReverseMinLabel.setVisible(false);
890        delayReverseReStartLabel.setVisible(false);
891        reverseDelayedRestartType.setVisible(false);
892        delayReverseReStartSensorLabel.setVisible(false);
893        delayReverseReStartSensor.setVisible(false);
894        delayReverseResetSensorBox.setVisible(false);
895        if (reverseAtEndBox.isSelected()) {
896            delayReverseReStartLabel.setVisible(true);
897            reverseDelayedRestartType.setVisible(true);
898            if (reverseDelayedRestartType.getSelectedItem().equals(Bundle.getMessage("DelayedStartTimed"))) {
899                delayReverseMinSpinner.setVisible(true);
900                delayReverseMinLabel.setVisible(true);
901            } else if (reverseDelayedRestartType.getSelectedItem().equals(Bundle.getMessage("DelayedStartSensor"))) {
902                delayReverseReStartSensor.setVisible(true);
903                delayReStartSensorLabel.setVisible(true);
904                delayReverseResetSensorBox.setVisible(true);
905            }
906        }
907        initiateFrame.pack();
908
909        if (resetWhenDoneBox.isSelected()) {
910            terminateWhenDoneBox.setSelected(false);
911            terminateWhenDoneBox.setEnabled(false);
912        } else {
913            terminateWhenDoneBox.setEnabled(true);
914        }
915    }
916
917    private void handleAutoRunClick(ActionEvent e) {
918        if (autoRunBox.isSelected()) {
919            showAutoRunItems();
920        } else {
921            hideAutoRunItems();
922        }
923        initiateFrame.pack();
924    }
925
926    private void handleStartingBlockSelectionChanged(ActionEvent e) {
927        initializeDestinationBlockCombo();
928        initiateFrame.pack();
929    }
930
931    private void handleAllocateAllTheWayButtonChanged(ActionEvent e) {
932        allocateCustomSpinner.setVisible(false);
933    }
934
935    private void handleAllocateBySafeButtonChanged(ActionEvent e) {
936        allocateCustomSpinner.setVisible(false);
937    }
938
939    private void handleAllocateNumberOfBlocksButtonChanged(ActionEvent e) {
940        allocateCustomSpinner.setVisible(true);
941    }
942
943    private void cancelInitiateTrain(ActionEvent e) {
944        _dispatcher.newTrainDone(null);
945    }
946
947    /*
948     * Handles press of "Add New Train" button.
949     * Move data to TrainInfo validating basic information
950     * Call dispatcher to start the train from traininfo which
951     * completes validation.
952     */
953    private void addNewTrain(ActionEvent e) {
954        try {
955            validateDialog();
956            dialogToTrainInfo(trainInfo);
957            _dispatcher.loadTrainFromTrainInfoThrowsException(trainInfo,"NONE","");
958        } catch (IllegalArgumentException ex) {
959            JmriJOptionPane.showMessageDialog(initiateFrame, ex.getMessage(),
960                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
961        }
962    }
963
964    private void initializeFreeTransitsCombo(List<Transit> transitList) {
965        Set<Transit> excludeTransits = new HashSet<>();
966        for (Transit t : _TransitManager.getNamedBeanSet()) {
967            if (t.getState() != Transit.IDLE) {
968                excludeTransits.add(t);
969            }
970        }
971        transitSelectBox.setExcludedItems(excludeTransits);
972        JComboBoxUtil.setupComboBoxMaxRows(transitSelectBox);
973
974        if (transitSelectBox.getItemCount() > 0) {
975            transitSelectBox.setSelectedIndex(0);
976            selectedTransit = transitSelectBox.getItemAt(0);
977        } else {
978            selectedTransit = null;
979        }
980    }
981
982    private void initializeFreeRosterEntriesCombo() {
983        rosterComboBox.update();
984        // remove used entries
985        for (int ix = rosterComboBox.getItemCount() - 1; ix > 1; ix--) {  // remove from back first item is the "select loco" message
986            if ( !_dispatcher.isAddressFree( ((RosterEntry)rosterComboBox.getItemAt(ix)).getDccLocoAddress().getNumber() ) ) {
987                rosterComboBox.removeItemAt(ix);
988            }
989        }
990    }
991
992    private void initializeFreeTrainsCombo() {
993        ActionListener[] als = trainSelectBox.getActionListeners();
994        for ( ActionListener al: als) {
995            trainSelectBox.removeActionListener(al);
996        }
997        trainSelectBox.removeAllItems();
998        trainSelectBox.addItem("Select Train");
999        // initialize free trains from operations
1000        List<Train> trains = jmri.InstanceManager.getDefault(TrainManager.class).getTrainsByNameList();
1001        if (trains.size() > 0) {
1002            for (int i = 0; i < trains.size(); i++) {
1003                Train t = trains.get(i);
1004                if (t != null) {
1005                    String tName = t.getName();
1006                    if (_dispatcher.isTrainFree(tName)) {
1007                        trainSelectBox.addItem(t);
1008                    }
1009                }
1010            }
1011        }
1012        for ( ActionListener al: als) {
1013            trainSelectBox.addActionListener(al);
1014        }
1015    }
1016
1017    /**
1018     * Sets the labels and inputs for speed profile running
1019     * @param b True if the roster entry has valid speed profile else false
1020     */
1021    private void setSpeedProfileOptions(TrainInfo info,boolean b) {
1022        useSpeedProfileLabel.setEnabled(b);
1023        useSpeedProfileCheckBox.setEnabled(b);
1024        stopBySpeedProfileLabel.setEnabled(b);
1025        stopBySpeedProfileCheckBox.setEnabled(b);
1026        stopBySpeedProfileAdjustLabel.setEnabled(b);
1027        stopBySpeedProfileAdjustSpinner.setEnabled(b);
1028        if (!b) {
1029            useSpeedProfileCheckBox.setSelected(false);
1030            stopBySpeedProfileCheckBox.setSelected(false);
1031            info.setUseSpeedProfile(false);
1032            info.setStopBySpeedProfile(false);
1033        }
1034    }
1035
1036
1037
1038    private void initializeStartingBlockCombo() {
1039        startingBlockBox.removeAllItems();
1040        startingBlockBoxList.clear();
1041        if (!inTransitBox.isSelected() && selectedTransit.getEntryBlocksList().isEmpty()) {
1042            inTransitBox.setSelected(true);
1043        }
1044        if (inTransitBox.isSelected()) {
1045            startingBlockBoxList = selectedTransit.getInternalBlocksList();
1046        } else {
1047            startingBlockBoxList = selectedTransit.getEntryBlocksList();
1048        }
1049        startingBlockSeqList = selectedTransit.getBlockSeqList();
1050        boolean found = false;
1051        for (int i = 0; i < startingBlockBoxList.size(); i++) {
1052            Block b = startingBlockBoxList.get(i);
1053            int seq = startingBlockSeqList.get(i).intValue();
1054            startingBlockBox.addItem(getBlockName(b) + "-" + seq);
1055            if (!found && b.getState() == Block.OCCUPIED) {
1056                startingBlockBox.setSelectedItem(getBlockName(b) + "-" + seq);
1057                found = true;
1058            }
1059        }
1060        JComboBoxUtil.setupComboBoxMaxRows(startingBlockBox);
1061    }
1062
1063    private void initializeDestinationBlockCombo() {
1064        destinationBlockBox.removeAllItems();
1065        destinationBlockBoxList.clear();
1066        int index = startingBlockBox.getSelectedIndex();
1067        if (index < 0) {
1068            return;
1069        }
1070        Block startBlock = startingBlockBoxList.get(index);
1071        destinationBlockBoxList = selectedTransit.getDestinationBlocksList(
1072                startBlock, inTransitBox.isSelected());
1073        destinationBlockSeqList = selectedTransit.getDestBlocksSeqList();
1074        for (int i = 0; i < destinationBlockBoxList.size(); i++) {
1075            Block b = destinationBlockBoxList.get(i);
1076            String bName = getBlockName(b);
1077            if (selectedTransit.getBlockCount(b) > 1) {
1078                int seq = destinationBlockSeqList.get(i).intValue();
1079                bName = bName + "-" + seq;
1080            }
1081            destinationBlockBox.addItem(bName);
1082        }
1083        JComboBoxUtil.setupComboBoxMaxRows(destinationBlockBox);
1084    }
1085
1086    private String getBlockName(Block b) {
1087        if (b != null) {
1088            return b.getDisplayName();
1089        }
1090        return " ";
1091    }
1092
1093    protected void showActivateFrame() {
1094        if (initiateFrame != null) {
1095            initializeFreeTransitsCombo(new ArrayList<Transit>());
1096            initiateFrame.setVisible(true);
1097        } else {
1098            _dispatcher.newTrainDone(null);
1099        }
1100    }
1101
1102    public void showActivateFrame(RosterEntry re) {
1103        showActivateFrame();
1104    }
1105
1106    private void loadTrainInfo(ActionEvent e) {
1107        String[] names = _tiFile.getTrainInfoFileNames();
1108        if (names.length > 0) {
1109            //prompt user to select a single train info filename from directory list
1110            Object selName = JmriJOptionPane.showInputDialog(initiateFrame,
1111                    Bundle.getMessage("LoadTrainChoice"), Bundle.getMessage("LoadTrainTitle"),
1112                    JmriJOptionPane.QUESTION_MESSAGE, null, names, names[0]);
1113            if ((selName == null) || (((String) selName).equals(""))) {
1114                return;
1115            }
1116            //read xml data from selected filename and move it into the new train dialog box
1117            _trainInfoName = (String) selName;
1118            try {
1119                trainInfo = _tiFile.readTrainInfo((String) selName);
1120                if (trainInfo != null) {
1121                    // process the information just read
1122                    trainInfoToDialog(trainInfo);
1123                }
1124            } catch (java.io.IOException ioe) {
1125                log.error("IO Exception when reading train info file", ioe);
1126            } catch (org.jdom2.JDOMException jde) {
1127                log.error("JDOM Exception when reading train info file", jde);
1128            }
1129        }
1130        handleDelayStartClick(null);
1131        handleReverseAtEndBoxClick(null);
1132    }
1133
1134    private void saveTrainInfo(ActionEvent e) {
1135        try {
1136            dialogToTrainInfo(trainInfo);
1137        } catch (IllegalArgumentException ide) {
1138            JmriJOptionPane.showMessageDialog(initiateFrame, ide.getMessage(),
1139                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
1140            return;
1141        }
1142        // get file name
1143        String eName = "";
1144        eName = JmriJOptionPane.showInputDialog(initiateFrame,
1145                Bundle.getMessage("EnterFileName") + " :", _trainInfoName);
1146        if (eName == null) {  //Cancel pressed
1147            return;
1148        }
1149        if (eName.length() < 1) {
1150            JmriJOptionPane.showMessageDialog(initiateFrame, Bundle.getMessage("Error25"),
1151                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
1152            return;
1153        }
1154        String fileName = normalizeXmlFileName(eName);
1155        _trainInfoName = fileName;
1156        // check if train info file name is in use
1157        String[] names = _tiFile.getTrainInfoFileNames();
1158        if (names.length > 0) {
1159            boolean found = false;
1160            for (int i = 0; i < names.length; i++) {
1161                if (fileName.equals(names[i])) {
1162                    found = true;
1163                }
1164            }
1165            if (found) {
1166                // file by that name is already present
1167                int selectedValue = JmriJOptionPane.showOptionDialog(initiateFrame,
1168                        Bundle.getMessage("Question3", fileName),
1169                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.DEFAULT_OPTION,
1170                        JmriJOptionPane.QUESTION_MESSAGE, null,
1171                        new Object[]{Bundle.getMessage("ButtonReplace"),Bundle.getMessage("ButtonNo")},
1172                        Bundle.getMessage("ButtonNo"));
1173                if (selectedValue != 0 ) { // array position 0 , replace not selected
1174                    return;   // return without writing if "No" response
1175                }
1176            }
1177        }
1178        // write the Train Info file
1179        try {
1180            _tiFile.writeTrainInfo(trainInfo, fileName);
1181        } //catch (org.jdom2.JDOMException jde) {
1182        // log.error("JDOM exception writing Train Info: "+jde);
1183        //}
1184        catch (java.io.IOException ioe) {
1185            log.error("IO exception writing Train Info", ioe);
1186        }
1187    }
1188
1189    private void saveTrainInfoAsTemplate(ActionEvent e) {
1190        try {
1191            dialogToTrainInfo(trainInfo);
1192        } catch (IllegalArgumentException ide) {
1193            JmriJOptionPane.showMessageDialog(initiateFrame, ide.getMessage(),
1194                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
1195            return;
1196        }
1197        // get file name
1198        String fileName = normalizeXmlFileName(nameOfTemplateFile);
1199        // check if train info file name is in use
1200        String[] names = _tiFile.getTrainInfoFileNames();
1201        if (names.length > 0) {
1202            boolean found = false;
1203            for (int i = 0; i < names.length; i++) {
1204                if (fileName.equals(names[i])) {
1205                    found = true;
1206                }
1207            }
1208            if (found) {
1209                // file by that name is already present
1210                int selectedValue = JmriJOptionPane.showOptionDialog(initiateFrame,
1211                        Bundle.getMessage("Question3", fileName),
1212                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.DEFAULT_OPTION,
1213                        JmriJOptionPane.QUESTION_MESSAGE, null,
1214                        new Object[]{Bundle.getMessage("ButtonReplace"),Bundle.getMessage("ButtonNo")},
1215                        Bundle.getMessage("ButtonNo"));
1216                if (selectedValue != 0 ) { // array position 0 , replace not selected
1217                    return;   // return without writing if "No" response
1218                }
1219            }
1220        }
1221        // write the Train Info file
1222        try {
1223            _tiFile.writeTrainInfo(trainInfo, fileName);
1224        } //catch (org.jdom2.JDOMException jde) {
1225        // log.error("JDOM exception writing Train Info: "+jde);
1226        //}
1227        catch (java.io.IOException ioe) {
1228            log.error("IO exception writing Train Info", ioe);
1229        }
1230    }
1231
1232    private void deleteTrainInfo(ActionEvent e) {
1233        String[] names = _tiFile.getTrainInfoFileNames();
1234        if (names.length > 0) {
1235            Object selName = JmriJOptionPane.showInputDialog(initiateFrame,
1236                    Bundle.getMessage("DeleteTrainChoice"), Bundle.getMessage("DeleteTrainTitle"),
1237                    JmriJOptionPane.QUESTION_MESSAGE, null, names, names[0]);
1238            if ((selName == null) || (((String) selName).equals(""))) {
1239                return;
1240            }
1241            _tiFile.deleteTrainInfoFile((String) selName);
1242        }
1243    }
1244
1245    private void trainInfoToDialog(TrainInfo info) {
1246        if (!info.getTransitName().isEmpty()) {
1247            try {
1248                transitSelectBox.setSelectedItemByName(info.getTransitName());
1249            } catch (Exception ex) {
1250                log.warn("Transit {} from file not in Transit menu", info.getTransitName());
1251                JmriJOptionPane.showMessageDialog(initiateFrame,
1252                        Bundle.getMessage("TransitWarn", info.getTransitName()),
1253                        null, JmriJOptionPane.WARNING_MESSAGE);
1254            }
1255        }
1256        switch (info.getTrainsFrom()) {
1257            case TRAINSFROMROSTER:
1258                radioTrainsFromRoster.setSelected(true);
1259                if (!info.getRosterId().isEmpty()) {
1260                    if (!setRosterComboBox(rosterComboBox, info.getRosterId())) {
1261                        log.warn("Roster {} from file not in Roster Combo", info.getRosterId());
1262                        JmriJOptionPane.showMessageDialog(initiateFrame,
1263                                Bundle.getMessage("TrainWarn", info.getRosterId()),
1264                                null, JmriJOptionPane.WARNING_MESSAGE);
1265                    }
1266                }
1267                break;
1268            case TRAINSFROMOPS:
1269                radioTrainsFromOps.setSelected(true);
1270                if (!info.getTrainName().isEmpty()) {
1271                    if (!setTrainComboBox(trainSelectBox, info.getTrainName())) {
1272                        log.warn("Train {} from file not in Train Combo", info.getTrainName());
1273                        JmriJOptionPane.showMessageDialog(initiateFrame,
1274                                Bundle.getMessage("TrainWarn", info.getTrainName()),
1275                                null, JmriJOptionPane.WARNING_MESSAGE);
1276                    }
1277                }
1278                break;
1279            case TRAINSFROMUSER:
1280                radioTrainsFromUser.setSelected(true);
1281                dccAddressSpinner.setValue(Integer.parseInt(info.getDccAddress()));
1282                break;
1283            case TRAINSFROMSETLATER:
1284            default:
1285                radioTrainsFromSetLater.setSelected(true);
1286        }
1287        trainNameField.setText(info.getTrainUserName());
1288        trainDetectionComboBox.setSelectedItemByValue(info.getTrainDetection());
1289        inTransitBox.setSelected(info.getTrainInTransit());
1290        initializeStartingBlockCombo();
1291        initializeDestinationBlockCombo();
1292        setComboBox(startingBlockBox, info.getStartBlockName());
1293        setComboBox(destinationBlockBox, info.getDestinationBlockName());
1294        setAllocateMethodButtons(info.getAllocationMethod());
1295        prioritySpinner.setValue(info.getPriority());
1296        resetWhenDoneBox.setSelected(info.getResetWhenDone());
1297        reverseAtEndBox.setSelected(info.getReverseAtEnd());
1298        setDelayModeBox(info.getDelayedStart(), delayedStartBox);
1299        //delayedStartBox.setSelected(info.getDelayedStart());
1300        departureHrSpinner.setValue(info.getDepartureTimeHr());
1301        departureMinSpinner.setValue(info.getDepartureTimeMin());
1302        delaySensor.setSelectedItem(info.getDelaySensor());
1303        resetStartSensorBox.setSelected(info.getResetStartSensor());
1304        setDelayModeBox(info.getDelayedRestart(), delayedReStartBox);
1305        delayMinSpinner.setValue(info.getRestartDelayMin());
1306        delayReStartSensor.setSelectedItem(info.getRestartSensor());
1307        resetRestartSensorBox.setSelected(info.getResetRestartSensor());
1308
1309        resetStartSensorBox.setSelected(info.getResetStartSensor());
1310        setDelayModeBox(info.getReverseDelayedRestart(), reverseDelayedRestartType);
1311        delayReverseMinSpinner.setValue(info.getReverseRestartDelayMin());
1312        delayReverseReStartSensor.setSelectedItem(info.getReverseRestartSensor());
1313        delayReverseResetSensorBox.setSelected(info.getReverseResetRestartSensor());
1314
1315        terminateWhenDoneBox.setSelected(info.getTerminateWhenDone());
1316        nextTrain.setSelectedIndex(-1);
1317
1318        try {
1319            nextTrain.setSelectedItem(info.getNextTrain());
1320        } catch (Exception ex){
1321            nextTrain.setSelectedIndex(-1);
1322        }
1323        handleTerminateWhenDoneBoxClick(null);
1324        setComboBox(trainTypeBox, info.getTrainType());
1325        autoRunBox.setSelected(info.getAutoRun());
1326        loadAtStartupBox.setSelected(info.getLoadAtStartup());
1327        setAllocateMethodButtons(info.getAllocationMethod());
1328        autoTrainInfoToDialog(info);
1329    }
1330
1331    private boolean validateDialog() throws IllegalArgumentException {
1332        int index = transitSelectBox.getSelectedIndex();
1333        if (index < 0) {
1334            throw new IllegalArgumentException(Bundle.getMessage("Error44"));
1335        }
1336        switch (trainsFromButtonGroup.getSelection().getActionCommand()) {
1337            case "TRAINSFROMROSTER":
1338                if (rosterComboBox.getSelectedIndex() < 1 ) {
1339                    throw new IllegalArgumentException(Bundle.getMessage("Error41"));
1340                }
1341                break;
1342            case "TRAINSFROMOPS":
1343                if (trainSelectBox.getSelectedIndex() < 1) {
1344                    throw new IllegalArgumentException(Bundle.getMessage("Error42"));
1345                }
1346                break;
1347            case "TRAINSFROMUSER":
1348                if (trainNameField.getText().isEmpty()) {
1349                    throw new IllegalArgumentException(Bundle.getMessage("Error22"));
1350                }
1351                break;
1352            case "TRAINSFROMSETLATER":
1353            default:
1354        }
1355        index = startingBlockBox.getSelectedIndex();
1356        if (index < 0) {
1357            throw new IllegalArgumentException(Bundle.getMessage("Error13"));
1358        }
1359        index = destinationBlockBox.getSelectedIndex();
1360        if (index < 0) {
1361            throw new IllegalArgumentException(Bundle.getMessage("Error8"));
1362        }
1363        if ((!reverseAtEndBox.isSelected()) && resetWhenDoneBox.isSelected()
1364                && (!selectedTransit.canBeResetWhenDone())) {
1365            resetWhenDoneBox.setSelected(false);
1366            throw new IllegalArgumentException(Bundle.getMessage("NoResetMessage"));
1367        }
1368        int max = Math.round((float) maxSpeedSpinner.getValue()*100.0f);
1369        int min = Math.round((float) minReliableOperatingSpeedSpinner.getValue()*100.0f);
1370        if ((max-min) < 10) {
1371            throw new IllegalArgumentException(Bundle.getMessage("Error49",
1372                    maxSpeedSpinner.getValue(), minReliableOperatingSpeedSpinner.getValue()));
1373        }
1374        return true;
1375    }
1376
1377    private boolean dialogToTrainInfo(TrainInfo info) {
1378        int index = transitSelectBox.getSelectedIndex();
1379        if (index >= 0 ) {
1380            info.setTransitName(transitSelectBox.getSelectedItem().getDisplayName());
1381            info.setTransitId(transitSelectBox.getSelectedItem().getDisplayName());
1382        }
1383        switch (trainsFromButtonGroup.getSelection().getActionCommand()) {
1384            case "TRAINSFROMROSTER":
1385                info.setRosterId(((RosterEntry) rosterComboBox.getSelectedItem()).getId());
1386                info.setDccAddress(((RosterEntry) rosterComboBox.getSelectedItem()).getDccAddress());
1387                trainInfo.setTrainsFrom(TrainsFrom.TRAINSFROMROSTER);
1388                setTrainsFromOptions(trainInfo.getTrainsFrom());
1389                break;
1390            case "TRAINSFROMOPS":
1391                info.setTrainName(((Train) trainSelectBox.getSelectedItem()).getId());
1392                info.setDccAddress(String.valueOf(dccAddressSpinner.getValue()));
1393                trainInfo.setTrainsFrom(TrainsFrom.TRAINSFROMUSER);
1394                setTrainsFromOptions(trainInfo.getTrainsFrom());
1395                break;
1396            case "TRAINSFROMUSER":
1397                info.setDccAddress(String.valueOf(dccAddressSpinner.getValue()));
1398                break;
1399            case "TRAINSFROMSETLATER":
1400            default:
1401                info.setTrainName("");
1402                info.setDccAddress("");
1403        }
1404        info.setTrainUserName(trainNameField.getText());
1405        info.setTrainInTransit(inTransitBox.isSelected());
1406        info.setStartBlockName((String) startingBlockBox.getSelectedItem());
1407        index = startingBlockBox.getSelectedIndex();
1408        info.setStartBlockId(startingBlockBoxList.get(index).getDisplayName());
1409        info.setStartBlockSeq(startingBlockSeqList.get(index).intValue());
1410        info.setDestinationBlockName((String) destinationBlockBox.getSelectedItem());
1411        index = destinationBlockBox.getSelectedIndex();
1412        info.setDestinationBlockId(destinationBlockBoxList.get(index).getDisplayName());
1413        info.setDestinationBlockSeq(destinationBlockSeqList.get(index).intValue());
1414        info.setPriority((Integer) prioritySpinner.getValue());
1415        info.setTrainDetection(((TrainDetectionItem)trainDetectionComboBox.getSelectedItem()).value);
1416        info.setResetWhenDone(resetWhenDoneBox.isSelected());
1417        info.setReverseAtEnd(reverseAtEndBox.isSelected());
1418        info.setDelayedStart(delayModeFromBox(delayedStartBox));
1419        info.setDelaySensorName(delaySensor.getSelectedItemDisplayName());
1420        info.setResetStartSensor(resetStartSensorBox.isSelected());
1421        info.setDepartureTimeHr((Integer) departureHrSpinner.getValue());
1422        info.setDepartureTimeMin((Integer) departureMinSpinner.getValue());
1423        info.setTrainType((String) trainTypeBox.getSelectedItem());
1424        info.setAutoRun(autoRunBox.isSelected());
1425        info.setLoadAtStartup(loadAtStartupBox.isSelected());
1426        info.setAllocateAllTheWay(false); // force to false next field is now used.
1427        if (allocateAllTheWayRadioButton.isSelected()) {
1428            info.setAllocationMethod(ActiveTrain.ALLOCATE_AS_FAR_AS_IT_CAN);
1429        } else if (allocateBySafeRadioButton.isSelected()) {
1430            info.setAllocationMethod(ActiveTrain.ALLOCATE_BY_SAFE_SECTIONS);
1431        } else {
1432            info.setAllocationMethod((Integer) allocateCustomSpinner.getValue());
1433        }
1434        info.setDelayedRestart(delayModeFromBox(delayedReStartBox));
1435        info.setRestartSensorName(delayReStartSensor.getSelectedItemDisplayName());
1436        info.setResetRestartSensor(resetRestartSensorBox.isSelected());
1437        info.setRestartDelayMin((Integer) delayMinSpinner.getValue());
1438
1439        info.setReverseDelayedRestart(delayModeFromBox(reverseDelayedRestartType));
1440        info.setReverseRestartSensorName(delayReverseReStartSensor.getSelectedItemDisplayName());
1441        info.setReverseResetRestartSensor(delayReverseResetSensorBox.isSelected());
1442        info.setReverseRestartDelayMin((Integer) delayReverseMinSpinner.getValue());
1443
1444        info.setTerminateWhenDone(terminateWhenDoneBox.isSelected());
1445        if (nextTrain.getSelectedIndex() > 0 ) {
1446            info.setNextTrain((String)nextTrain.getSelectedItem());
1447        } else {
1448            info.setNextTrain("None");
1449        }
1450        autoRunItemsToTrainInfo(info);
1451        return true;
1452    }
1453
1454    private boolean setRosterComboBox(RosterEntryComboBox box, String txt) {
1455        boolean found = false;
1456        for (int i = 1; i < box.getItemCount(); i++) {
1457            if (txt.equals(((RosterEntry) box.getItemAt(i)).getId())) {
1458                box.setSelectedIndex(i);
1459                found = true;
1460                break;
1461            }
1462        }
1463        if (!found && box.getItemCount() > 0) {
1464            box.setSelectedIndex(0);
1465        }
1466        return found;
1467    }
1468
1469    // Normalizes a suggested xml file name.  Returns null string if a valid name cannot be assembled
1470    private String normalizeXmlFileName(String name) {
1471        if (name.length() < 1) {
1472            return "";
1473        }
1474        String newName = name;
1475        // strip off .xml or .XML if present
1476        if ((name.endsWith(".xml")) || (name.endsWith(".XML"))) {
1477            newName = name.substring(0, name.length() - 4);
1478            if (newName.length() < 1) {
1479                return "";
1480            }
1481        }
1482        // replace all non-alphanumeric characters with underscore
1483        newName = newName.replaceAll("[\\W]", "_");
1484        return (newName + ".xml");
1485    }
1486
1487    private boolean setTrainComboBox(JComboBox<Object> box, String txt) {
1488        boolean found = false;
1489        for (int i = 1; i < box.getItemCount(); i++) { //skip the select train item
1490            if (txt.equals(box.getItemAt(i).toString())) {
1491                box.setSelectedIndex(i);
1492                found = true;
1493                break;
1494            }
1495        }
1496        if (!found && box.getItemCount() > 0) {
1497            box.setSelectedIndex(0);
1498        }
1499        return found;
1500    }
1501
1502    private boolean setComboBox(JComboBox<String> box, String txt) {
1503        boolean found = false;
1504        for (int i = 0; i < box.getItemCount(); i++) {
1505            if (txt.equals(box.getItemAt(i))) {
1506                box.setSelectedIndex(i);
1507                found = true;
1508                break;
1509            }
1510        }
1511        if (!found && box.getItemCount() > 0) {
1512            box.setSelectedIndex(0);
1513        }
1514        return found;
1515    }
1516
1517    int delayModeFromBox(JComboBox<String> box) {
1518        String mode = (String) box.getSelectedItem();
1519        int result = jmri.util.StringUtil.getStateFromName(mode, delayedStartInt, delayedStartString);
1520
1521        if (result < 0) {
1522            log.warn("unexpected mode string in turnoutMode: {}", mode);
1523            throw new IllegalArgumentException();
1524        }
1525        return result;
1526    }
1527
1528    void setDelayModeBox(int mode, JComboBox<String> box) {
1529        String result = jmri.util.StringUtil.getNameFromState(mode, delayedStartInt, delayedStartString);
1530        box.setSelectedItem(result);
1531    }
1532
1533    /**
1534     * The following are for items that are only for automatic running of
1535     * ActiveTrains They are isolated here to simplify changing them in the
1536     * future.
1537     * <ul>
1538     * <li>initializeAutoRunItems - initializes the display of auto run items in
1539     * this window
1540     * <li>initializeAutoRunValues - initializes the values of auto run items
1541     * from values in a saved train info file hideAutoRunItems - hides all auto
1542     * run items in this window showAutoRunItems - shows all auto run items in
1543     * this window
1544     * <li>autoTrainInfoToDialog - gets auto run items from a train info, puts
1545     * values in items, and initializes auto run dialog items
1546     * <li>autoTrainItemsToTrainInfo - copies values of auto run items to train
1547     * info for saving to a file
1548     * <li>readAutoRunItems - reads and checks values of all auto run items.
1549     * returns true if OK, sends appropriate messages and returns false if not
1550     * OK
1551     * <li>setAutoRunItems - sets the user entered auto run items in the new
1552     * AutoActiveTrain
1553     * </ul>
1554     */
1555    // auto run items in ActivateTrainFrame
1556    private final JPanel pa1 = new JPanel();
1557    private final JLabel speedFactorLabel = new JLabel(Bundle.getMessage("SpeedFactorLabel"));
1558    private final JSpinner speedFactorSpinner = new JSpinner();
1559    private final JLabel minReliableOperatingSpeedLabel = new JLabel(Bundle.getMessage("MinReliableOperatingSpeedLabel"));
1560    private final JSpinner minReliableOperatingSpeedSpinner = new JSpinner();
1561    private final JLabel maxSpeedLabel = new JLabel(Bundle.getMessage("MaxSpeedLabel"));
1562    private final JSpinner maxSpeedSpinner = new JSpinner();
1563    private final JPanel pa2 = new JPanel();
1564    private final JLabel rampRateLabel = new JLabel(Bundle.getMessage("RampRateBoxLabel"));
1565    private final JComboBox<String> rampRateBox = new JComboBox<>();
1566    private final JPanel pa2a = new JPanel();
1567    private final JLabel useSpeedProfileLabel = new JLabel(Bundle.getMessage("UseSpeedProfileLabel"));
1568    private final JCheckBox useSpeedProfileCheckBox = new JCheckBox( );
1569    private final JLabel stopBySpeedProfileLabel = new JLabel(Bundle.getMessage("StopBySpeedProfileLabel"));
1570    private final JCheckBox stopBySpeedProfileCheckBox = new JCheckBox( );
1571    private final JLabel stopBySpeedProfileAdjustLabel = new JLabel(Bundle.getMessage("StopBySpeedProfileAdjustLabel"));
1572    private final JSpinner stopBySpeedProfileAdjustSpinner = new JSpinner();
1573    private final JPanel pa3 = new JPanel();
1574    private final JCheckBox soundDecoderBox = new JCheckBox(Bundle.getMessage("SoundDecoder"));
1575    private final JCheckBox runInReverseBox = new JCheckBox(Bundle.getMessage("RunInReverse"));
1576    private final JPanel pa4 = new JPanel();
1577
1578    protected static class TrainDetectionJCombo extends JComboBox<TrainDetectionItem> {
1579        public void setSelectedItemByValue(TrainDetection var) {
1580            for ( int ix = 0; ix < getItemCount() ; ix ++ ) {
1581                if (getItemAt(ix).value == var) {
1582                    this.setSelectedIndex(ix);
1583                    break;
1584                }
1585            }
1586        }
1587    }
1588    private final JLabel trainDetectionLabel = new JLabel(Bundle.getMessage("TrainDetection"));
1589    public final TrainDetectionJCombo trainDetectionComboBox
1590    = new TrainDetectionJCombo();
1591
1592    protected static class TrainLengthUnitsJCombo extends JComboBox<TrainLengthUnitsItem> {
1593        public void setSelectedItemByValue(TrainLengthUnits var) {
1594            for ( int ix = 0; ix < getItemCount() ; ix ++ ) {
1595                if (getItemAt(ix).value == var) {
1596                    this.setSelectedIndex(ix);
1597                    break;
1598                }
1599            }
1600        }
1601    }
1602    public final TrainLengthUnitsJCombo trainLengthUnitsComboBox = new TrainLengthUnitsJCombo();
1603    private final JLabel trainLengthLabel = new JLabel(Bundle.getMessage("MaxTrainLengthLabel"));
1604    private JLabel trainLengthAltLengthLabel;
1605    private final JSpinner maxTrainLengthSpinner = new JSpinner(); // initialized later
1606
1607    private void initializeAutoRunItems() {
1608        initializeRampCombo();
1609        initializeScaleLengthBox();
1610        pa1.setLayout(new FlowLayout());
1611        pa1.add(speedFactorLabel);
1612        speedFactorSpinner.setModel(new SpinnerNumberModel(Float.valueOf(1.0f), Float.valueOf(0.1f), Float.valueOf(2.0f), Float.valueOf(0.01f)));
1613        speedFactorSpinner.setEditor(new JSpinner.NumberEditor(speedFactorSpinner, "# %"));
1614        pa1.add(speedFactorSpinner);
1615        speedFactorSpinner.setToolTipText(Bundle.getMessage("SpeedFactorHint"));
1616        pa1.add(new JLabel("   "));
1617        pa1.add(maxSpeedLabel);
1618        maxSpeedSpinner.setModel(new SpinnerNumberModel(Float.valueOf(1.0f), Float.valueOf(0.1f), Float.valueOf(1.0f), Float.valueOf(0.01f)));
1619        maxSpeedSpinner.setEditor(new JSpinner.NumberEditor(maxSpeedSpinner, "# %"));
1620        pa1.add(maxSpeedSpinner);
1621        maxSpeedSpinner.setToolTipText(Bundle.getMessage("MaxSpeedHint"));
1622        pa1.add(minReliableOperatingSpeedLabel);
1623        minReliableOperatingSpeedSpinner.setModel(new SpinnerNumberModel(Float.valueOf(0.0f), Float.valueOf(0.0f), Float.valueOf(1.0f), Float.valueOf(0.01f)));
1624        minReliableOperatingSpeedSpinner.setEditor(new JSpinner.NumberEditor(minReliableOperatingSpeedSpinner, "# %"));
1625        pa1.add(minReliableOperatingSpeedSpinner);
1626        minReliableOperatingSpeedSpinner.setToolTipText(Bundle.getMessage("MinReliableOperatingSpeedHint"));
1627        initiatePane.add(pa1);
1628        pa2.setLayout(new FlowLayout());
1629        pa2.add(rampRateLabel);
1630        pa2.add(rampRateBox);
1631        rampRateBox.setToolTipText(Bundle.getMessage("RampRateBoxHint"));
1632        pa2.add(useSpeedProfileLabel);
1633        pa2.add(useSpeedProfileCheckBox);
1634        useSpeedProfileCheckBox.setToolTipText(Bundle.getMessage("UseSpeedProfileHint"));
1635        initiatePane.add(pa2);
1636        pa2a.setLayout(new FlowLayout());
1637        pa2a.add(stopBySpeedProfileLabel);
1638        pa2a.add(stopBySpeedProfileCheckBox);
1639        stopBySpeedProfileCheckBox.setToolTipText(Bundle.getMessage("UseSpeedProfileHint")); // reuse identical hint for Stop
1640        pa2a.add(stopBySpeedProfileAdjustLabel);
1641        stopBySpeedProfileAdjustSpinner.setModel(new SpinnerNumberModel( Float.valueOf(1.0f), Float.valueOf(0.1f), Float.valueOf(5.0f), Float.valueOf(0.01f)));
1642        stopBySpeedProfileAdjustSpinner.setEditor(new JSpinner.NumberEditor(stopBySpeedProfileAdjustSpinner, "# %"));
1643        pa2a.add(stopBySpeedProfileAdjustSpinner);
1644        stopBySpeedProfileAdjustSpinner.setToolTipText(Bundle.getMessage("StopBySpeedProfileAdjustHint"));
1645        initiatePane.add(pa2a);
1646        pa3.setLayout(new FlowLayout());
1647        pa3.add(soundDecoderBox);
1648        soundDecoderBox.setToolTipText(Bundle.getMessage("SoundDecoderBoxHint"));
1649        pa3.add(new JLabel("   "));
1650        pa3.add(runInReverseBox);
1651        runInReverseBox.setToolTipText(Bundle.getMessage("RunInReverseBoxHint"));
1652        initiatePane.add(pa3);
1653        maxTrainLengthSpinner.setModel(new SpinnerNumberModel(Float.valueOf(18.0f), Float.valueOf(0.0f), Float.valueOf(10000.0f), Float.valueOf(0.5f)));
1654        maxTrainLengthSpinner.setEditor(new JSpinner.NumberEditor(maxTrainLengthSpinner, "###0.0"));
1655        maxTrainLengthSpinner.setToolTipText(Bundle.getMessage("MaxTrainLengthHint")); // won't be updated while Dispatcher is open
1656        maxTrainLengthSpinner.addChangeListener(new ChangeListener() {
1657            @Override
1658            public void stateChanged(ChangeEvent e) {
1659                handlemaxTrainLengthChangeUnitsLength(e);
1660            }
1661        });
1662        trainLengthUnitsComboBox.addActionListener(new ActionListener() {
1663            @Override
1664            public void actionPerformed(ActionEvent e) {
1665                handlemaxTrainLengthChangeUnitsLength(e);
1666            }
1667        });
1668        trainLengthAltLengthLabel=new JLabel();
1669        pa4.setLayout(new FlowLayout());
1670        pa4.add(trainLengthLabel);
1671        pa4.add(maxTrainLengthSpinner);
1672        pa4.add(trainLengthUnitsComboBox);
1673        pa4.add(trainLengthAltLengthLabel);
1674        initiatePane.add(pa4);
1675        hideAutoRunItems();   // initialize with auto run items hidden
1676    }
1677
1678    private void handlemaxTrainLengthChangeUnitsLength(Object e) {
1679        trainLengthAltLengthLabel.setText(maxTrainLengthCalculateAltFormatted(
1680                ((TrainLengthUnitsItem) trainLengthUnitsComboBox.getSelectedItem()).getValue(),
1681                (float) maxTrainLengthSpinner.getValue()));
1682    }
1683
1684    private String maxTrainLengthCalculateAltFormatted(TrainLengthUnits fromUnits, float fromValue) {
1685        float value = maxTrainLengthCalculateAlt(fromUnits, fromValue);
1686        switch (fromUnits) {
1687            case TRAINLENGTH_ACTUALINCHS:
1688                return String.format("%.2f %s", value, Bundle.getMessage("TrainLengthInScaleFeet"));
1689            case TRAINLENGTH_ACTUALCM:
1690                return String.format("%.1f %s", value, Bundle.getMessage("TrainLengthInScaleMeters"));
1691            case TRAINLENGTH_SCALEFEET:
1692                return String.format("%.1f %s", value, Bundle.getMessage("TrainLengthInActualInchs"));
1693            case TRAINLENGTH_SCALEMETERS:
1694                return String.format("%.0f %s", value, Bundle.getMessage("TrainLengthInActualcm"));
1695            default:
1696                log.error("Invalid TrainLengthUnits must have been updated, fix maxTrainLengthCalculateAltFormatted");
1697        }
1698        return "";
1699    }
1700
1701    private float maxTrainLengthToScaleMeters(TrainLengthUnits fromUnits, float fromValue) {
1702        float value;
1703        // convert to meters.
1704        switch (fromUnits) {
1705            case TRAINLENGTH_ACTUALINCHS:
1706                value = fromValue / 12.0f * (float) _dispatcher.getScale().getScaleRatio();
1707                value = value / 3.28084f;
1708                break;
1709            case TRAINLENGTH_ACTUALCM:
1710                value = fromValue / 100.0f * (float) _dispatcher.getScale().getScaleRatio();
1711                break;
1712           case TRAINLENGTH_SCALEFEET:
1713               value = fromValue / 3.28084f;
1714               break;
1715           case TRAINLENGTH_SCALEMETERS:
1716               value = fromValue;
1717               break;
1718           default:
1719               value = 0;
1720               log.error("Invalid TrainLengthUnits has been updated, fix me");
1721        }
1722        return value;
1723    }
1724
1725    /*
1726     * Calculates the reciprocal unit. Actual to Scale and vice versa
1727     */
1728    private float maxTrainLengthCalculateAlt(TrainLengthUnits fromUnits, float fromValue) {
1729        switch (fromUnits) {
1730            case TRAINLENGTH_ACTUALINCHS:
1731                // calc scale feet
1732                return (float) jmri.util.MathUtil.granulize(fromValue / 12 * (float) _dispatcher.getScale().getScaleRatio(),0.1f);
1733            case TRAINLENGTH_ACTUALCM:
1734                // calc scale meter
1735                return fromValue / 100 * (float) _dispatcher.getScale().getScaleRatio();
1736            case TRAINLENGTH_SCALEFEET:
1737                // calc actual inchs
1738                return fromValue * 12 * (float) _dispatcher.getScale().getScaleFactor();
1739           case TRAINLENGTH_SCALEMETERS:
1740                // calc actual cm.
1741                return fromValue * 100 * (float) _dispatcher.getScale().getScaleFactor();
1742           default:
1743               log.error("Invalid TrainLengthUnits has been updated, fix me");
1744        }
1745        return 0;
1746    }
1747
1748    private void hideAutoRunItems() {
1749        pa1.setVisible(false);
1750        pa2.setVisible(false);
1751        pa2a.setVisible(false);
1752        pa3.setVisible(false);
1753        pa4.setVisible(false);
1754    }
1755
1756    private void showAutoRunItems() {
1757        pa1.setVisible(true);
1758        pa2.setVisible(true);
1759        pa2a.setVisible(true);
1760        pa3.setVisible(true);
1761        pa4.setVisible(true);
1762    }
1763
1764    private void autoTrainInfoToDialog(TrainInfo info) {
1765        speedFactorSpinner.setValue(info.getSpeedFactor());
1766        maxSpeedSpinner.setValue(info.getMaxSpeed());
1767        minReliableOperatingSpeedSpinner.setValue(info.getMinReliableOperatingSpeed());
1768        setComboBox(rampRateBox, info.getRampRate());
1769        trainDetectionComboBox.setSelectedItemByValue(info.getTrainDetection());
1770        runInReverseBox.setSelected(info.getRunInReverse());
1771        soundDecoderBox.setSelected(info.getSoundDecoder());
1772        trainLengthUnitsComboBox.setSelectedItemByValue(info.getTrainLengthUnits());
1773        switch (info.getTrainLengthUnits()) {
1774            case TRAINLENGTH_SCALEFEET:
1775                maxTrainLengthSpinner.setValue(info.getMaxTrainLengthScaleFeet());
1776                break;
1777            case TRAINLENGTH_SCALEMETERS:
1778                maxTrainLengthSpinner.setValue(info.getMaxTrainLengthScaleMeters());
1779                break;
1780            case TRAINLENGTH_ACTUALINCHS:
1781                maxTrainLengthSpinner.setValue(info.getMaxTrainLengthScaleFeet() * 12.0f * (float)_dispatcher.getScale().getScaleFactor());
1782                break;
1783            case TRAINLENGTH_ACTUALCM:
1784                maxTrainLengthSpinner.setValue(info.getMaxTrainLengthScaleMeters() * 100.0f * (float)_dispatcher.getScale().getScaleFactor());
1785                break;
1786            default:
1787                maxTrainLengthSpinner.setValue(0.0f);
1788        }
1789        useSpeedProfileCheckBox.setSelected(info.getUseSpeedProfile());
1790        stopBySpeedProfileCheckBox.setSelected(info.getStopBySpeedProfile());
1791        stopBySpeedProfileAdjustSpinner.setValue(info.getStopBySpeedProfileAdjust());
1792        if (autoRunBox.isSelected()) {
1793            showAutoRunItems();
1794        } else {
1795            hideAutoRunItems();
1796        }
1797        initiateFrame.pack();
1798    }
1799
1800    private void autoRunItemsToTrainInfo(TrainInfo info) {
1801        info.setSpeedFactor((float) speedFactorSpinner.getValue());
1802        info.setMaxSpeed((float) maxSpeedSpinner.getValue());
1803        info.setMinReliableOperatingSpeed((float) minReliableOperatingSpeedSpinner.getValue());
1804        info.setRampRate((String) rampRateBox.getSelectedItem());
1805        info.setRunInReverse(runInReverseBox.isSelected());
1806        info.setSoundDecoder(soundDecoderBox.isSelected());
1807        info.setTrainLengthUnits(((TrainLengthUnitsItem) trainLengthUnitsComboBox.getSelectedItem()).getValue());
1808        info.setMaxTrainLengthScaleMeters(maxTrainLengthToScaleMeters( info.getTrainLengthUnits(), (float) maxTrainLengthSpinner.getValue()));
1809
1810        // Only use speed profile values if enabled
1811        if (useSpeedProfileCheckBox.isEnabled()) {
1812            info.setUseSpeedProfile(useSpeedProfileCheckBox.isSelected());
1813            info.setStopBySpeedProfile(stopBySpeedProfileCheckBox.isSelected());
1814            info.setStopBySpeedProfileAdjust((float) stopBySpeedProfileAdjustSpinner.getValue());
1815        } else {
1816            info.setUseSpeedProfile(false);
1817            info.setStopBySpeedProfile(false);
1818            info.setStopBySpeedProfileAdjust(1.0f);
1819        }
1820    }
1821
1822   private void initializeRampCombo() {
1823        rampRateBox.removeAllItems();
1824        rampRateBox.addItem(Bundle.getMessage("RAMP_NONE"));
1825        rampRateBox.addItem(Bundle.getMessage("RAMP_FAST"));
1826        rampRateBox.addItem(Bundle.getMessage("RAMP_MEDIUM"));
1827        rampRateBox.addItem(Bundle.getMessage("RAMP_MED_SLOW"));
1828        rampRateBox.addItem(Bundle.getMessage("RAMP_SLOW"));
1829        rampRateBox.addItem(Bundle.getMessage("RAMP_SPEEDPROFILE"));
1830        // Note: the order above must correspond to the numbers in AutoActiveTrain.java
1831    }
1832
1833    /**
1834     * Sets up the RadioButtons and visability of spinner for the allocation method
1835     *
1836     * @param value 0, Allocate by Safe spots, -1, allocate as far as possible Any
1837     *            other value the number of sections to allocate
1838     */
1839    private void setAllocateMethodButtons(int value) {
1840        switch (value) {
1841            case ActiveTrain.ALLOCATE_BY_SAFE_SECTIONS:
1842                allocateBySafeRadioButton.setSelected(true);
1843                allocateCustomSpinner.setVisible(false);
1844                break;
1845            case ActiveTrain.ALLOCATE_AS_FAR_AS_IT_CAN:
1846                allocateAllTheWayRadioButton.setSelected(true);
1847                allocateCustomSpinner.setVisible(false);
1848                break;
1849            default:
1850                allocateNumberOfBlocks.setSelected(true);
1851                allocateCustomSpinner.setVisible(true);
1852                allocateCustomSpinner.setValue(value);
1853        }
1854    }
1855
1856    /*
1857     * ComboBox item.
1858     */
1859    protected static class TrainDetectionItem {
1860        private String key;
1861        private TrainDetection value;
1862        public TrainDetectionItem(String text, TrainDetection trainDetection ) {
1863            this.key = text;
1864            this.value = trainDetection;
1865        }
1866        @Override
1867        public String toString()
1868        {
1869            return key;
1870        }
1871        public String getKey()
1872        {
1873            return key;
1874        }
1875        public TrainDetection getValue()
1876        {
1877            return value;
1878        }
1879    }
1880
1881    /*
1882     * ComboBox item.
1883     */
1884    protected static class TrainLengthUnitsItem {
1885        private String key;
1886        private TrainLengthUnits value;
1887        public TrainLengthUnitsItem(String text, TrainLengthUnits trainLength ) {
1888            this.key = text;
1889            this.value = trainLength;
1890        }
1891        @Override
1892        public String toString()
1893        {
1894            return key;
1895        }
1896        public String getKey()
1897        {
1898            return key;
1899        }
1900        public TrainLengthUnits getValue()
1901        {
1902            return value;
1903        }
1904    }
1905
1906    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActivateTrainFrame.class);
1907
1908}