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