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