001package jmri.jmrit.logix;
002
003import java.awt.BorderLayout;
004import java.awt.Color;
005import java.awt.Component;
006import java.awt.Dimension;
007import java.awt.FontMetrics;
008import java.awt.event.*;
009import java.awt.MouseInfo;
010import java.awt.Point;
011import java.awt.Rectangle;
012import java.util.ArrayList;
013import java.util.List;
014import java.util.ListIterator;
015
016import javax.swing.*;
017import javax.swing.table.*;
018import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
019
020import jmri.InstanceManager;
021import jmri.NamedBean;
022import jmri.NamedBeanHandle;
023import jmri.SpeedStepMode;
024import jmri.jmrit.picker.PickListModel;
025import jmri.util.ThreadingUtil;
026import jmri.jmrit.logix.ThrottleSetting.Command;
027import jmri.jmrit.logix.ThrottleSetting.CommandValue;
028import jmri.jmrit.logix.ThrottleSetting.ValueType;
029import jmri.util.swing.JmriJOptionPane;
030
031/**
032 * WarrantFame creates and edits Warrants <br>
033 * <hr>
034 * This file is part of JMRI.
035 * <p>
036 * JMRI is free software; you can redistribute it and/or modify it under the
037 * terms of version 2 of the GNU General Public License as published by the Free
038 * Software Foundation. See the "COPYING" file for a copy of this license.
039 * <p>
040 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
041 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
042 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
043 *
044 * @author Pete Cressman Copyright (C) 2009, 2010
045 */
046public class WarrantFrame extends WarrantRoute {
047
048    int _rowHeight;
049    private Warrant _warrant; // unregistered warrant - may be a copy of a
050                              // registered warrant
051    private Warrant _saveWarrant;
052    private ThrottleTableModel _commandModel;
053    private JTable _commandTable;
054    private JScrollPane _throttlePane;
055    Dimension _viewPortDim;
056
057    private ArrayList<ThrottleSetting> _throttleCommands = new ArrayList<>();
058    private long _startTime;
059    private float _speedFactor;
060    private float _speed;
061    private long _TTP = 0;
062    private boolean _forward = true;
063    LearnThrottleFrame _learnThrottle = null; // need access for JUnit test
064    static Color myGreen = new Color(0, 100, 0);
065
066    JTextField _sysNameBox;
067    JTextField _userNameBox;
068
069    JTabbedPane _tabbedPane;
070    JPanel _routePanel;
071    JPanel _commandPanel;
072    JPanel _parameterPanel;
073    JRadioButton _isSCWarrant = new JRadioButton(Bundle.getMessage("SmallLayoutTrainAutomater"), false);
074    JRadioButton _isWarrant = new JRadioButton(Bundle.getMessage("NormalWarrant"), true);
075    private DisplayButton _speedUnits;
076    private JLabel _unitsLabel;
077    private float _speedConversion;
078    JCheckBox _runForward = new JCheckBox(Bundle.getMessage("Forward"));
079    JFormattedTextField _speedFactorTextField = new JFormattedTextField();
080    JFormattedTextField _TTPtextField = new JFormattedTextField();
081    JCheckBox _noRampBox = new JCheckBox();
082    JCheckBox _shareRouteBox = new JCheckBox();
083    JCheckBox _addTracker = new JCheckBox();
084    JCheckBox _haltStartBox = new JCheckBox();
085    JCheckBox _runETOnlyBox = new JCheckBox();
086    JRadioButton _invisible = new JRadioButton();
087    JTextField _statusBox = new JTextField(90);
088    JRadioButton _showRoute = new JRadioButton(Bundle.getMessage("showRoute"), false);
089    JRadioButton _showScript = new JRadioButton(Bundle.getMessage("showScript"), false);
090
091    JTextField _searchStatus = new JTextField();
092    private boolean _dirty = false;
093
094    /*
095     * Constructor for opening an existing warrant for editing
096     */
097    protected WarrantFrame(Warrant w) {
098        super();
099        // w is registered
100        _saveWarrant = w;
101        // temp unregistered version until editing is saved.
102        _warrant = new Warrant(w.getSystemName(), w.getUserName());
103        setup(_saveWarrant, false);
104        init();
105    }
106
107    /*
108     * Constructor for creating a new warrant or copy or concatenation of
109     * warrants) Called by WarrantTableAction
110     */
111    protected WarrantFrame(Warrant startW, Warrant endW) {
112        super();
113        WarrantManager mgr = InstanceManager.getDefault(WarrantManager.class);
114        String sName = mgr.getAutoSystemName();
115        while (mgr.getBySystemName(sName) != null) {
116            mgr.updateAutoNumber(sName);
117            sName = mgr.getAutoSystemName();
118        }
119        _warrant = new Warrant(sName, null);
120        if (startW != null) {
121            if (endW != null) { // concatenate warrants
122                WarrantTableFrame tf = WarrantTableFrame.getDefault();
123                tf.setVisible(true);
124                boolean includeAllCmds = tf.askStopQuestion(startW.getLastOrder().getBlock().getDisplayName());
125                /*
126                if (JmriJOptionPane.showConfirmDialog(f, Bundle.getMessage("stopAtBlock",
127                        startW.getLastOrder().getBlock().getDisplayName()),
128                        Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,
129                        JmriJOptionPane.QUESTION_MESSAGE) == JmriJOptionPane.YES_OPTION) {
130                    includeAllCmds = true;
131                }*/
132                float entranceSpeed = setup(startW, !includeAllCmds);
133                List<BlockOrder> orders = endW.getBlockOrders();
134                BlockOrder bo = orders.get(0);    // block order of common midblock
135                bo.setExitName(endW.getfirstOrder().getExitName());
136                for (int i = 1; i < orders.size(); i++) {
137                    _orders.add(new BlockOrder(orders.get(i)));
138                }
139                _destination.setOrder(endW.getLastOrder());
140                if (_via.getOrder() == null) {
141                    _via.setOrder(endW.getViaOrder());
142                }
143                if (_avoid.getOrder() == null) {
144                    _avoid.setOrder(endW.getAvoidOrder());
145                }
146                float exitSpeed = 0;
147                NamedBean bean = bo.getBlock(); // common block
148                for (ThrottleSetting ts : endW.getThrottleCommands()) {
149                    if (includeAllCmds) {
150                        _throttleCommands.add(new ThrottleSetting(ts));
151                    } else {
152                        Command cmd = ts.getCommand();
153                        if (cmd.equals(Command.SPEED)) {
154                            exitSpeed = ts.getValue().getFloat();
155                        } else if (cmd.equals(Command.NOOP) && !ts.getBean().equals(bean)) {
156                            includeAllCmds = true;
157                            long et = _speedUtil.getTimeForDistance(entranceSpeed, bo.getPathLength()) / 2;
158                            RampData ramp = _speedUtil.getRampForSpeedChange(entranceSpeed, exitSpeed);
159                            String blockName = bean.getDisplayName();
160                            if (ramp.isUpRamp()) {
161                                ListIterator<Float> iter = ramp.speedIterator(true);
162                                while (iter.hasNext()) {
163                                    float speedSetting = iter.next().floatValue();
164                                    _throttleCommands.add(new ThrottleSetting(et, Command.SPEED, -1, ValueType.VAL_FLOAT,
165                                            SpeedStepMode.UNKNOWN, speedSetting, "", blockName, _speedUtil.getTrackSpeed(speedSetting)));
166                                    et = ramp.getRampTimeIncrement();
167                                }
168                            } else {
169                                ListIterator<Float> iter = ramp.speedIterator(false);
170                                while (iter.hasPrevious()) {
171                                    float speedSetting = iter.previous().floatValue();
172                                    _throttleCommands.add(new ThrottleSetting(et, Command.SPEED, -1, ValueType.VAL_FLOAT,
173                                            SpeedStepMode.UNKNOWN, speedSetting, "", blockName, _speedUtil.getTrackSpeed(speedSetting)));
174                                    et = ramp.getRampTimeIncrement();
175                                }
176                            }
177                            _throttleCommands.add(new ThrottleSetting(ts));
178                        }
179                    }
180                }
181            } else {    // else just copy startW
182                setup(startW, false);
183            }
184        } // else create new warrant
185        init();
186    }
187
188    /**
189     * Set up parameters from an existing warrant. note that _warrant is
190     * unregistered.
191     */
192    private float setup(Warrant warrant, boolean omitLastBlockCmds) {
193        _origin.setOrder(warrant.getfirstOrder());
194        _destination.setOrder(warrant.getLastOrder());
195        _via.setOrder(warrant.getViaOrder());
196        _avoid.setOrder(warrant.getAvoidOrder());
197        List<BlockOrder> list = warrant.getBlockOrders();
198        _orders = new ArrayList<>(list.size());
199        for (BlockOrder bo : list) {
200            _orders.add(new BlockOrder(bo));
201        }
202
203        if (warrant instanceof SCWarrant) {
204            _speedFactor = ((SCWarrant) warrant).getSpeedFactor();
205            _TTP = ((SCWarrant) warrant).getTimeToPlatform();
206            _forward = ((SCWarrant) warrant).getForward();
207        }
208
209        float entranceSpeed = 0;
210        for (ThrottleSetting ts : warrant.getThrottleCommands()) {
211            if (omitLastBlockCmds && list.size() > 0) {
212                NamedBean bean = list.get(list.size()-1).getBlock();
213                Command cmd = ts.getCommand();
214                if (cmd.equals(Command.SPEED)) {
215                    entranceSpeed = ts.getValue().getFloat();
216                }
217                _throttleCommands.add(new ThrottleSetting(ts));
218               if (cmd.equals(Command.NOOP) && ts.getBean().equals(bean)) {
219                     break;
220                }
221            } else {
222                _throttleCommands.add(new ThrottleSetting(ts));
223            }
224        }
225        _shareRouteBox.setSelected(warrant.getShareRoute());
226        _warrant.setShareRoute(warrant.getShareRoute());
227        _addTracker.setSelected(warrant.getAddTracker());
228        _warrant.setAddTracker(warrant.getAddTracker());
229        _haltStartBox.setSelected(warrant.getHaltStart());
230        _warrant.setHaltStart(warrant.getHaltStart());
231        _noRampBox.setSelected(warrant.getNoRamp());
232        _warrant.setNoRamp(warrant.getNoRamp());
233        _runETOnlyBox.setSelected(warrant.getRunBlind());
234        _warrant.setRunBlind(warrant.getRunBlind());
235        setTrainName(warrant.getTrainName());
236        _warrant.setTrainName(warrant.getTrainName());
237
238        SpeedUtil spU = warrant.getSpeedUtil();
239        setSpeedUtil(_warrant.getSpeedUtil());
240        _speedUtil.setDccAddress(spU.getDccAddress());
241        _speedUtil.setRosterId(spU.getRosterId());
242        if (_speedUtil.getDccAddress() != null) {
243            setTrainInfo(warrant.getTrainName());
244        } else {
245            setTrainName(warrant.getTrainName());
246        }
247        return entranceSpeed;
248    }
249
250    private void init() {
251        _commandModel = new ThrottleTableModel();
252
253        JPanel contentPane = new JPanel();
254        contentPane.setLayout(new BorderLayout(5, 5));
255
256        contentPane.add(makeTopPanel(), BorderLayout.NORTH);
257
258        _tabbedPane = new JTabbedPane();
259        _tabbedPane.addTab(Bundle.getMessage("MakeRoute"), makeFindRouteTabPanel());
260        _tabbedPane.addTab(Bundle.getMessage("RecordPlay"), makeSetPowerTabPanel());
261        contentPane.add(_tabbedPane, BorderLayout.CENTER);
262
263        contentPane.add(makeEditableButtonPanel(), BorderLayout.SOUTH);
264        if (_orders != null && !_orders.isEmpty()) {
265            _tabbedPane.setSelectedIndex(1);
266        }
267        if (!_throttleCommands.isEmpty()) {
268            _showScript.setSelected(true);
269        }
270        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
271        addWindowListener(new java.awt.event.WindowAdapter() {
272            @Override
273            public void windowClosing(java.awt.event.WindowEvent e) {
274                if (askClose()) {
275                    WarrantTableAction.getDefault().closeWarrantFrame();
276                }
277            }
278        });
279
280        makeMenus();
281        setTitle(Bundle.getMessage("editing", _warrant.getDisplayName()));
282        setContentPane(contentPane);
283        setVisible(true);
284        _parameterPanel.setMaximumSize(_parameterPanel.getPreferredSize());
285        _dirty = false;
286        pack();
287        getContentPane().addComponentListener(new ComponentAdapter() {
288            @Override
289            public void componentResized(ComponentEvent e) {
290                Component c = (Component) e.getSource();
291                int height = c.getHeight();
292                _viewPortDim.height = (_rowHeight * 10) + height - 541;
293                _throttlePane.getViewport().setPreferredSize(_viewPortDim);
294                _throttlePane.invalidate();
295                _commandTable.invalidate();
296            }
297        });
298        speedUnitsAction();
299    }
300
301    public boolean askClose() {
302        if (_dirty) {
303            // if runMode != MODE_NONE, this is probably a panic shutdown. Don't
304            // halt it.
305            if (JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("saveOrClose", _warrant.getDisplayName()),
306                    Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,
307                    JmriJOptionPane.QUESTION_MESSAGE) == JmriJOptionPane.YES_OPTION) {
308                if (!isRunning()) {
309                    save();
310                }
311            }
312        }
313        _dirty = false;
314        return true;
315    }
316
317    private JPanel makeTopPanel() {
318        JPanel topPanel = new JPanel();
319        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.PAGE_AXIS));
320
321        JPanel panel = new JPanel();
322        panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
323        panel.add(Box.createHorizontalStrut(2 * STRUT_SIZE));
324        panel.add(new JLabel(Bundle.getMessage("LabelSystemName")));
325        panel.add(Box.createHorizontalStrut(STRUT_SIZE));
326        if (_saveWarrant != null) {
327            _sysNameBox = new JTextField(_saveWarrant.getSystemName());
328            _sysNameBox.setEditable(false);
329            _userNameBox = new JTextField(_saveWarrant.getUserName());
330        } else {
331            _sysNameBox = new JTextField(_warrant.getSystemName());
332            _userNameBox = new JTextField(_warrant.getUserName());
333        }
334        _sysNameBox.setBackground(Color.white);
335        panel.add(_sysNameBox);
336        panel.add(Box.createHorizontalStrut(2 * STRUT_SIZE));
337        panel.add(new JLabel(Bundle.getMessage("LabelUserName")));
338        panel.add(Box.createHorizontalStrut(STRUT_SIZE));
339        panel.add(_userNameBox);
340        panel.add(Box.createHorizontalStrut(2 * STRUT_SIZE));
341        topPanel.add(panel);
342        topPanel.add(Box.createVerticalStrut(STRUT_SIZE));
343
344        return topPanel;
345    }
346
347    private JPanel makeFindRouteTabPanel() {
348        JPanel tab1 = new JPanel();
349        tab1.setLayout(new BoxLayout(tab1, BoxLayout.LINE_AXIS));
350        tab1.add(Box.createHorizontalStrut(STRUT_SIZE));
351
352        JPanel topLeft = new JPanel();
353        topLeft.setLayout(new BoxLayout(topLeft, BoxLayout.PAGE_AXIS));
354
355        topLeft.add(makeBlockPanels(false));
356
357        topLeft.add(Box.createVerticalStrut(2 * STRUT_SIZE));
358        tab1.add(topLeft);
359
360        tab1.add(Box.createHorizontalStrut(STRUT_SIZE));
361        JPanel topRight = new JPanel();
362        topRight.setLayout(new BoxLayout(topRight, BoxLayout.LINE_AXIS));
363
364        JPanel panel = new JPanel();
365        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
366        panel.add(Box.createVerticalStrut(2 * STRUT_SIZE));
367        panel.add(calculatePanel(true));
368        panel.add(Box.createVerticalStrut(2 * STRUT_SIZE));
369        panel.add(searchDepthPanel(true));
370
371        JPanel p = new JPanel();
372        p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
373        p.add(makeTextBoxPanel(true, _searchStatus, "SearchRoute", null));
374        _searchStatus.setEditable(false);
375        p.add(Box.createVerticalGlue());
376        panel.add(p);
377
378        _searchStatus.setBackground(Color.white);
379        _searchStatus.setEditable(false);
380        panel.add(Box.createRigidArea(new Dimension(10,
381                topLeft.getPreferredSize().height - panel.getPreferredSize().height)));
382        panel.add(Box.createVerticalStrut(STRUT_SIZE));
383        panel.add(Box.createVerticalGlue());
384        topRight.add(panel);
385        topRight.add(Box.createHorizontalStrut(STRUT_SIZE));
386
387        PickListModel<OBlock> pickListModel = PickListModel.oBlockPickModelInstance();
388        topRight.add(new JScrollPane(pickListModel.makePickTable()));
389        Dimension dim = topRight.getPreferredSize();
390        topRight.setMinimumSize(dim);
391        tab1.add(topRight);
392        tab1.add(Box.createHorizontalStrut(STRUT_SIZE));
393        return tab1;
394    }
395
396    private JPanel makeSetPowerTabPanel() {
397        JPanel tab2 = new JPanel();
398        tab2.setLayout(new BoxLayout(tab2, BoxLayout.PAGE_AXIS));
399        tab2.add(makeTabMidPanel());
400
401        _parameterPanel = new JPanel();
402        _parameterPanel.setLayout(new BoxLayout(_parameterPanel, BoxLayout.LINE_AXIS));
403
404        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
405        _parameterPanel.add(makeBorderedTrainPanel());
406        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
407        JPanel typePanel = makeTypePanel();
408        JPanel edge = new JPanel();
409        edge.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK),
410                Bundle.getMessage("SelectType"),
411                javax.swing.border.TitledBorder.CENTER,
412                javax.swing.border.TitledBorder.TOP));
413        edge.add(typePanel);
414        _parameterPanel.add(edge);
415        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
416
417        JPanel scParamPanel = makeSCParamPanel();
418        edge = new JPanel();
419        edge.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK),
420                Bundle.getMessage("SetSCParameters"),
421                javax.swing.border.TitledBorder.CENTER,
422                javax.swing.border.TitledBorder.TOP));
423        edge.add(scParamPanel);
424        _parameterPanel.add(edge);
425        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
426
427        JPanel learnPanel = makeRecordPanel();
428        edge = new JPanel();
429        edge.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK),
430                Bundle.getMessage("LearnMode"),
431                javax.swing.border.TitledBorder.CENTER,
432                javax.swing.border.TitledBorder.TOP));
433        edge.add(learnPanel);
434        _parameterPanel.add(edge);
435        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
436
437        JPanel paramsPanel = makeRunParmsPanel();
438        edge = new JPanel();
439        edge.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK),
440                Bundle.getMessage("RunParameters"),
441                javax.swing.border.TitledBorder.CENTER,
442                javax.swing.border.TitledBorder.TOP));
443        edge.add(paramsPanel);
444        _parameterPanel.add(edge);
445        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
446
447        JPanel runPanel = makePlaybackPanel();
448        edge = new JPanel();
449        edge.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK),
450                Bundle.getMessage("RunTrain"),
451                javax.swing.border.TitledBorder.CENTER,
452                javax.swing.border.TitledBorder.TOP));
453        edge.add(runPanel);
454        _parameterPanel.add(edge);
455        _parameterPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
456        _parameterPanel.setPreferredSize(_parameterPanel.getPreferredSize());
457        tab2.add(_parameterPanel);
458
459        _isSCWarrant.addActionListener((ActionEvent e) -> {
460            setPanelEnabled(scParamPanel, true);
461            setPanelEnabled(learnPanel, false);
462            setPanelEnabled(paramsPanel, false);
463            setPanelEnabled(runPanel, false);
464        });
465        if (_saveWarrant != null && _saveWarrant instanceof SCWarrant) {
466            setPanelEnabled(scParamPanel, true);
467            setPanelEnabled(learnPanel, false);
468            setPanelEnabled(paramsPanel, false);
469            setPanelEnabled(runPanel, false);
470            _isSCWarrant.setVisible(true);
471        }
472
473        _isWarrant.addActionListener((ActionEvent e) -> {
474            setPanelEnabled(scParamPanel, false);
475            setPanelEnabled(learnPanel, true);
476            setPanelEnabled(paramsPanel, true);
477            setPanelEnabled(runPanel, true);
478        });
479
480        JPanel panel = new JPanel();
481        panel.add(makeTextBoxPanel(false, _statusBox, "Status", null));
482        _statusBox.setEditable(false);
483        _statusBox.setMinimumSize(new Dimension(300, _statusBox.getPreferredSize().height));
484        _statusBox.setMaximumSize(new Dimension(900, _statusBox.getPreferredSize().height));
485        panel.add(_statusBox);
486        tab2.add(panel);
487
488        return tab2;
489    }
490
491    private void setPanelEnabled(JPanel panel, Boolean isEnabled) {
492        panel.setEnabled(isEnabled);
493
494        Component[] components = panel.getComponents();
495
496        for (Component component : components) {
497            if ("javax.swing.JPanel".equals(component.getClass().getName())) {
498                setPanelEnabled((JPanel) component, isEnabled);
499            }
500            component.setEnabled(isEnabled);
501        }
502    }
503
504    private JPanel makeBorderedTrainPanel() {
505        JPanel trainPanel = makeTrainIdPanel(null);
506
507        JPanel edge = new JPanel();
508        edge.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK),
509                Bundle.getMessage("SetPower"),
510                javax.swing.border.TitledBorder.CENTER,
511                javax.swing.border.TitledBorder.TOP));
512        edge.add(trainPanel);
513        return edge;
514    }
515
516    private JPanel makeTypePanel() {
517        JPanel typePanel = new JPanel();
518        typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.LINE_AXIS));
519        typePanel.add(Box.createHorizontalStrut(STRUT_SIZE));
520
521        JPanel wTypePanel = new JPanel();
522        wTypePanel.setLayout(new BoxLayout(wTypePanel, BoxLayout.PAGE_AXIS));
523        wTypePanel.add(Box.createVerticalStrut(STRUT_SIZE));
524        ButtonGroup group = new ButtonGroup();
525        group.add(_isSCWarrant);
526        group.add(_isWarrant);
527        _isSCWarrant.setToolTipText(Bundle.getMessage("SCW_Tooltip"));
528        _isWarrant.setToolTipText(Bundle.getMessage("W_Tooltip"));
529        wTypePanel.add(_isSCWarrant);
530        wTypePanel.add(_isWarrant);
531        typePanel.add(wTypePanel);
532        return typePanel;
533    }
534
535    private void addSpeeds() {
536        float speed = 0.0f;
537        for (ThrottleSetting ts : _throttleCommands) {
538            CommandValue cmdVal = ts.getValue();
539            ValueType valType = cmdVal.getType();
540            switch (valType) {
541                case VAL_FLOAT:
542                    speed = _speedUtil.getTrackSpeed(cmdVal.getFloat());
543                    break;
544                case VAL_TRUE:
545                    _speedUtil.setIsForward(true);
546                    break;
547                case VAL_FALSE:
548                    _speedUtil.setIsForward(false);
549                    break;
550                default:
551            }
552            ts.setTrackSpeed(speed);
553        }
554        _commandModel.fireTableDataChanged();
555        showCommands(true);
556    }
557
558    private JPanel makeSCParamPanel() {
559        JPanel scParamPanel = new JPanel();
560        scParamPanel.setLayout(new BoxLayout(scParamPanel, BoxLayout.PAGE_AXIS));
561        scParamPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
562
563        scParamPanel.add(_runForward);
564        _runForward.setSelected(_forward);
565
566        JPanel ttpPanel = new JPanel();
567        ttpPanel.setLayout(new BoxLayout(ttpPanel, BoxLayout.LINE_AXIS));
568        JLabel ttp_l = new JLabel(Bundle.getMessage("TTP"));
569        _TTPtextField.setValue(_TTP);
570        _TTPtextField.setColumns(6);
571        ttp_l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
572        _TTPtextField.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
573        ttpPanel.add(Box.createVerticalStrut(STRUT_SIZE));
574        ttpPanel.add(ttp_l);
575        ttpPanel.add(_TTPtextField);
576        ttpPanel.setToolTipText(Bundle.getMessage("TTPtoolTip"));
577        scParamPanel.add(ttpPanel);
578
579        JPanel sfPanel = new JPanel();
580        sfPanel.setLayout(new BoxLayout(sfPanel, BoxLayout.LINE_AXIS));
581        JLabel sf_l = new JLabel(Bundle.getMessage("SF"));
582        _speedFactorTextField.setValue((long) (100 * _speedFactor));
583        _speedFactorTextField.setColumns(3);
584        sf_l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
585        _speedFactorTextField.setAlignmentX(JComponent.RIGHT_ALIGNMENT);
586        sfPanel.add(Box.createVerticalStrut(STRUT_SIZE));
587        sfPanel.add(sf_l);
588        sfPanel.add(_speedFactorTextField);
589        sfPanel.setToolTipText(Bundle.getMessage("sfToolTip"));
590        scParamPanel.add(sfPanel);
591
592        if (_isWarrant.isSelected()) {
593            setPanelEnabled(scParamPanel, false);
594        }
595        return scParamPanel;
596    }
597
598    private JPanel makeRecordPanel() {
599        JPanel learnPanel = new JPanel();
600        learnPanel.setLayout(new BoxLayout(learnPanel, BoxLayout.LINE_AXIS));
601        learnPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
602
603        JPanel startStopPanel = new JPanel();
604        startStopPanel.setLayout(new BoxLayout(startStopPanel, BoxLayout.PAGE_AXIS));
605        startStopPanel.add(Box.createVerticalStrut(STRUT_SIZE));
606        JButton startButton = new JButton(Bundle.getMessage("Start"));
607        startButton.addActionListener((ActionEvent e) -> {
608            clearTempWarrant();
609            _tabbedPane.setSelectedIndex(1);
610            showCommands(true);
611            runLearnModeTrain();
612        });
613        JButton stopButton = new JButton(Bundle.getMessage("Stop"));
614        stopButton.addActionListener((ActionEvent e) -> {
615            stopRunTrain(false);
616        });
617        startButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
618        stopButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
619        startStopPanel.add(startButton);
620        startStopPanel.add(Box.createVerticalStrut(STRUT_SIZE));
621        startStopPanel.add(stopButton);
622        startStopPanel.add(Box.createRigidArea(new Dimension(30 + stopButton.getPreferredSize().width, 10)));
623        learnPanel.add(startStopPanel);
624
625        return learnPanel;
626    }
627
628    private JPanel makeRunParmsPanel() {
629        JPanel paramsPanel = new JPanel();
630        paramsPanel.setLayout(new BoxLayout(paramsPanel, BoxLayout.LINE_AXIS));
631        paramsPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
632
633        JPanel panel = new JPanel();
634        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
635        panel.add(Box.createVerticalStrut(STRUT_SIZE));
636        panel.add(makeTextBoxPanel(_shareRouteBox, "ShareRoute", "ToolTipShareRoute"));
637        panel.add(makeTextBoxPanel(_addTracker, "AddTracker", "ToolTipAddTracker"));
638        panel.add(makeTextBoxPanel(_noRampBox, "NoRamping", "ToolTipNoRamping"));
639        panel.add(makeTextBoxPanel(_haltStartBox, "HaltAtStart", null));
640        panel.add(makeTextBoxPanel(_runETOnlyBox, "RunETOnly", "ToolTipRunETOnly"));
641
642        paramsPanel.add(panel);
643        return paramsPanel;
644    }
645
646    private JPanel makePlaybackPanel() {
647        JPanel runPanel = new JPanel();
648        runPanel.setLayout(new BoxLayout(runPanel, BoxLayout.LINE_AXIS));
649        runPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
650
651        JPanel panel = new JPanel();
652        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
653        runPanel.add(panel);
654        runPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
655
656        JRadioButton run = new JRadioButton(Bundle.getMessage("ARun"), false);
657        JRadioButton halt = new JRadioButton(Bundle.getMessage("Stop"), false);
658        JRadioButton resume = new JRadioButton(Bundle.getMessage("Resume"), false);
659        JRadioButton eStop = new JRadioButton(Bundle.getMessage("EStop"), false);
660        JRadioButton abort = new JRadioButton(Bundle.getMessage("Abort"), false);
661
662        panel = new JPanel();
663        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
664        ButtonGroup group = new ButtonGroup();
665        group.add(run);
666        group.add(halt);
667        group.add(resume);
668        group.add(eStop);
669        group.add(abort);
670        group.add(_invisible);
671        panel.add(run);
672        panel.add(halt);
673        panel.add(resume);
674        panel.add(eStop);
675        panel.add(abort);
676        runPanel.add(panel);
677
678        run.addActionListener((ActionEvent e) -> {
679            runTrain();
680        });
681        halt.addActionListener((ActionEvent e) -> {
682            doControlCommand(Warrant.HALT);
683        });
684        resume.addActionListener((ActionEvent e) -> {
685            doControlCommand(Warrant.RESUME);
686        });
687        eStop.addActionListener((ActionEvent e) -> {
688            doControlCommand(Warrant.ESTOP);
689        });
690        abort.addActionListener((ActionEvent e) -> {
691            doControlCommand(Warrant.ABORT);
692        });
693        runPanel.add(panel);
694        return runPanel;
695    }
696
697    private JPanel makeTabMidPanel() {
698        JPanel midPanel = new JPanel();
699        midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.PAGE_AXIS));
700
701        JPanel tablePanel = new JPanel();
702        tablePanel.setLayout(new BoxLayout(tablePanel, BoxLayout.LINE_AXIS));
703        tablePanel.add(Box.createHorizontalStrut(5));
704        _routePanel = makeRouteTablePanel();
705        tablePanel.add(_routePanel);
706        tablePanel.add(makeThrottleTablePanel());
707        JPanel buttonPanel = new JPanel();
708        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
709        ButtonGroup group = new ButtonGroup();
710        group.add(_showRoute);
711        group.add(_showScript);
712        buttonPanel.add(_showRoute);
713        buttonPanel.add(_showScript);
714        boolean show = (!_throttleCommands.isEmpty());
715        showCommands(show);
716        _showScript.setSelected(show);
717        _showRoute.addActionListener((ActionEvent e) -> {
718            showCommands(false);
719        });
720        _showScript.addActionListener((ActionEvent e) -> {
721            showCommands(true);
722        });
723
724        if (_saveWarrant != null && _saveWarrant instanceof SCWarrant) {
725            _showRoute.setSelected(true);
726            showCommands(false);
727            setPanelEnabled(buttonPanel, false);
728        }
729        _isSCWarrant.addActionListener((ActionEvent e) -> {
730            _showRoute.setSelected(true);
731            showCommands(false);
732            setPanelEnabled(buttonPanel, false);
733        });
734        _isWarrant.addActionListener((ActionEvent e) -> {
735            setPanelEnabled(buttonPanel, true);
736        });
737
738        midPanel.add(buttonPanel);
739        midPanel.add(Box.createVerticalStrut(STRUT_SIZE));
740        midPanel.add(tablePanel);
741        midPanel.add(Box.createVerticalStrut(STRUT_SIZE));
742
743        return midPanel;
744    }
745
746    private void showCommands(boolean setCmds) {
747        _routePanel.setVisible(!setCmds);
748        _commandPanel.setVisible(setCmds);
749    }
750
751    private void speedUnitsAction() {
752        switch (_displayPref) {
753            case MPH:
754                _displayPref = Display.KPH;
755                _speedConversion = _scale * 3.6f;
756                setFormatter("kph");
757                break;
758            case KPH:
759                _displayPref = Display.MMPS;
760                _speedConversion = 1000;
761                _unitsLabel.setText(Bundle.getMessage("trackSpeed"));
762                setFormatter("mmps");
763                break;
764            case MMPS:
765                _displayPref = Display.INPS;
766                _speedConversion = 39.37f;
767                setFormatter("inps");
768                break;
769            case INPS:
770            default:
771                _displayPref = Display.MPH;
772                _speedConversion = 2.23694f * _scale;
773                _unitsLabel.setText(Bundle.getMessage("scaleSpeed"));
774                setFormatter("mph");
775                break;
776        }
777        _speedUnits.setDisplayPref(_displayPref);
778        addSpeeds();
779    }
780
781    private void setFormatter(String title) {
782        JTableHeader header = _commandTable.getTableHeader();
783        TableColumnModel colMod = header.getColumnModel();
784        TableColumn tabCol = colMod.getColumn(ThrottleTableModel.SPEED_COLUMN);
785        tabCol.setHeaderValue(Bundle.getMessage(title));
786        header.repaint();
787    }
788
789    private JPanel makeThrottleTablePanel() {
790        _commandTable = new JTable(_commandModel);
791        DefaultCellEditor ed = (DefaultCellEditor) _commandTable.getDefaultEditor(String.class);
792        ed.setClickCountToStart(1);
793
794        TableColumnModel columnModel = _commandTable.getColumnModel();
795        for (int i = 0; i < _commandModel.getColumnCount(); i++) {
796            int width = _commandModel.getPreferredWidth(i);
797            columnModel.getColumn(i).setPreferredWidth(width);
798        }
799        TableColumn cmdColumn = columnModel.getColumn(ThrottleTableModel.COMMAND_COLUMN);
800        cmdColumn.setCellEditor(new CommandCellEditor(new JComboBox<>()));
801        cmdColumn.setCellRenderer(new CommandCellRenderer());
802        cmdColumn.setMinWidth(40);
803
804        TableColumn valueColumn = columnModel.getColumn(ThrottleTableModel.VALUE_COLUMN);
805        valueColumn.setCellEditor(new ValueCellEditor(new JTextField()));
806
807        _throttlePane = new JScrollPane(_commandTable);
808        _viewPortDim = _commandTable.getPreferredSize();
809        _rowHeight = _commandTable.getRowHeight();
810        _viewPortDim.height = _rowHeight * 10;
811        _throttlePane.getViewport().setPreferredSize(_viewPortDim);
812
813        JPanel buttonPanel = new JPanel();
814        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
815        buttonPanel.add(Box.createVerticalStrut(2 * STRUT_SIZE));
816
817        JButton insertButton = new JButton(Bundle.getMessage("buttonInsertRow"));
818        insertButton.addActionListener((ActionEvent e) -> {
819            insertRow();
820        });
821        buttonPanel.add(insertButton);
822        buttonPanel.add(Box.createVerticalStrut(2 * STRUT_SIZE));
823
824        JButton deleteButton = new JButton(Bundle.getMessage("buttonDeleteRow"));
825        deleteButton.addActionListener((ActionEvent e) -> {
826            deleteRow();
827        });
828        buttonPanel.add(deleteButton);
829        buttonPanel.add(Box.createVerticalStrut(2 * STRUT_SIZE));
830
831        if (_displayPref.equals(Display.MMPS) || _displayPref.equals(Display.INPS)) {
832            _unitsLabel = new JLabel(Bundle.getMessage("trackSpeed"));
833        } else {
834            _unitsLabel = new JLabel(Bundle.getMessage("scaleSpeed"));
835        }
836        _unitsLabel.setHorizontalAlignment(SwingConstants.CENTER);
837
838        _speedUnits = new DisplayButton(_displayPref);
839        FontMetrics fm = _speedUnits.getFontMetrics(_speedUnits.getFont());
840        int width = Math.max(fm.stringWidth(Display.KPH.toString()),
841                Math.max(fm.stringWidth(Display.MPH.toString()),
842                        fm.stringWidth(Display.MMPS.toString())));
843        Dimension d = _speedUnits.getPreferredSize();
844        d.width = width + 40;
845        _speedUnits.setMaximumSize(d);
846        _speedUnits.setMinimumSize(d);
847        _speedUnits.setPreferredSize(d);
848        _speedUnits.addActionListener((ActionEvent evt) -> speedUnitsAction());
849
850        buttonPanel.add(_unitsLabel);
851        buttonPanel.add(_speedUnits);
852
853        _commandPanel = new JPanel();
854        _commandPanel.setLayout(new BoxLayout(_commandPanel, BoxLayout.PAGE_AXIS));
855        JLabel title = new JLabel(Bundle.getMessage("CommandTableTitle"));
856        JPanel panel = new JPanel();
857        panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
858        JPanel p = new JPanel();
859        p.add(_throttlePane);
860        panel.add(p);
861        buttonPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
862        panel.add(buttonPanel);
863        buttonPanel.add(Box.createHorizontalStrut(STRUT_SIZE));
864        _commandPanel.add(title);
865        _commandPanel.add(panel);
866        _commandPanel.add(Box.createGlue());
867        _displayPref = Display.KPH;
868        return _commandPanel;
869    }
870
871    private void insertRow() {
872        int row = _commandTable.getSelectedRow();
873        if (row < 0) {
874            showWarning(Bundle.getMessage("selectRow"));
875            return;
876        }
877        row++;
878        _throttleCommands.add(row, new ThrottleSetting());
879        _commandModel.fireTableDataChanged();
880        _commandTable.setRowSelectionInterval(row, row);
881    }
882
883    private void deleteRow() {
884        int row = _commandTable.getSelectedRow();
885        if (row < 0) {
886            showWarning(Bundle.getMessage("selectRow"));
887            return;
888        }
889        ThrottleSetting cmd = _throttleCommands.get(row);
890        if (cmd != null && cmd.getCommand() != null) {
891            if (cmd.getCommand().equals(Command.NOOP)) {
892                showWarning(Bundle.getMessage("cannotDeleteNoop"));
893                return;
894            }
895            long time = cmd.getTime();
896            if ((row + 1) < _throttleCommands.size()) {
897                time += _throttleCommands.get(row + 1).getTime();
898                _throttleCommands.get(row + 1).setTime(time);
899            }
900        }
901        _throttleCommands.remove(row);
902        _dirty = true;
903        _commandModel.fireTableDataChanged();
904    }
905
906    /**
907     * Save, Cancel, Delete buttons
908     */
909    private JPanel makeEditableButtonPanel() {
910        JPanel buttonPanel = new JPanel();
911        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
912        buttonPanel.add(Box.createHorizontalStrut(10 * STRUT_SIZE));
913
914        JPanel panel = new JPanel();
915        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
916        JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
917        saveButton.addActionListener((ActionEvent e) -> {
918            if (save()) {
919                WarrantTableAction.getDefault().closeWarrantFrame();
920            }
921        });
922        panel.add(saveButton);
923        panel.add(Box.createVerticalStrut(STRUT_SIZE));
924        buttonPanel.add(panel);
925        buttonPanel.add(Box.createHorizontalStrut(3 * STRUT_SIZE));
926
927        panel = new JPanel();
928        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
929        JButton copyButton = new JButton(Bundle.getMessage("ButtonCopy"));
930        copyButton.addActionListener((ActionEvent e) -> {
931            WarrantTableAction.getDefault().makeWarrantFrame(_saveWarrant, null);
932        });
933        panel.add(copyButton);
934        panel.add(Box.createVerticalStrut(STRUT_SIZE));
935        buttonPanel.add(panel);
936        buttonPanel.add(Box.createHorizontalStrut(3 * STRUT_SIZE));
937
938        panel = new JPanel();
939        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
940        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
941        cancelButton.addActionListener((ActionEvent e) -> {
942            close();
943        });
944        panel.add(cancelButton);
945        panel.add(Box.createVerticalStrut(STRUT_SIZE));
946        buttonPanel.add(panel);
947        buttonPanel.add(Box.createHorizontalStrut(3 * STRUT_SIZE));
948
949        buttonPanel.add(Box.createHorizontalGlue());
950        return buttonPanel;
951    }
952
953    private void doControlCommand(int cmd) {
954        if (log.isDebugEnabled()) {
955            log.debug("actionPerformed on doControlCommand  cmd= {}", cmd);
956        }
957        int runMode = _warrant.getRunMode();
958        if (runMode == Warrant.MODE_NONE) {
959            JmriJOptionPane.showMessageDialog(this,
960                    Bundle.getMessage("NotRunning", _warrant.getDisplayName()),
961                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
962        } else if (runMode == Warrant.MODE_LEARN && cmd != Warrant.ABORT) {
963            JmriJOptionPane.showMessageDialog(this,
964                    Bundle.getMessage("LearnInvalidControl", _warrant.getDisplayName()),
965                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
966        } else {
967            _warrant.controlRunTrain(cmd);
968        }
969        _invisible.setSelected(true);
970    }
971
972    private void makeMenus() {
973        setTitle(Bundle.getMessage("TitleWarrant", _warrant.getDisplayName()));
974        JMenuBar menuBar = new JMenuBar();
975        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
976        fileMenu.add(new jmri.configurexml.StoreMenu());
977        menuBar.add(fileMenu);
978        setJMenuBar(menuBar);
979        addHelpMenu("package.jmri.jmrit.logix.CreateEditWarrant", true);
980    }
981
982    private void clearCommands() {
983        _throttleCommands = new ArrayList<>();
984        _commandModel.fireTableDataChanged();
985        _searchStatus.setText("");
986    }
987
988    @Override
989    protected void selectedRoute(ArrayList<BlockOrder> orders) {
990        clearCommands();
991        _tabbedPane.setSelectedIndex(1);
992    }
993
994    /**
995     * Sets address and block orders and does checks Non-null return is fatal
996     */
997    private String checkTrainId() {
998        String msg = setAddress(); // sets SpeedUtil address in 'this'
999                                   // (WarrantRoute)
1000        if (msg == null) {
1001            msg = routeIsValid();
1002        }
1003        if (msg == null) {
1004            _warrant.setBlockOrders(getOrders());
1005            msg = _warrant.checkforTrackers();
1006        }
1007        if (msg == null) {
1008            msg = checkLocoAddress();
1009        }
1010        return msg;
1011    }
1012
1013    private String checkThrottleCommands() {
1014        if (_throttleCommands.size() <= getOrders().size() + 1) {
1015            return Bundle.getMessage("NoCommands", _warrant.getDisplayName());
1016        }
1017        float lastSpeed = 0.0f;
1018        for (int i = 0; i < _throttleCommands.size(); i++) {
1019            ThrottleSetting ts = _throttleCommands.get(i);
1020            Command cmd = ts.getCommand();
1021            CommandValue val = ts.getValue();
1022            if (val == null || cmd == null) {
1023                return Bundle.getMessage("BadThrottleSetting", i + 1);
1024            }
1025            ValueType valType = val.getType();
1026            if (valType == null) {
1027                return Bundle.getMessage("BadThrottleSetting", i + 1);
1028            }
1029            switch (cmd) {
1030                case SPEED:
1031                    if (valType != ValueType.VAL_FLOAT) {
1032                        return Bundle.getMessage("badThrottleCommand",
1033                                i + 1, cmd.toString(), valType.toString());
1034                    }
1035                    lastSpeed = ts.getValue().getFloat();
1036                    if (lastSpeed > 1) {
1037                        return Bundle.getMessage("badSpeed", lastSpeed);
1038                    } else if (lastSpeed < 0) { // EStop OK only in the last
1039                                                // block
1040                        OBlock blk = getOrders().get(getOrders().size() - 1).getBlock();
1041                        if (blk == null || !blk.getSystemName().equals(ts.getBeanSystemName())) {
1042                            return Bundle.getMessage("badSpeed", lastSpeed);
1043                        }
1044                    }
1045                    break;
1046                case NOOP:
1047                    if (valType != ValueType.VAL_NOOP) {
1048                        return Bundle.getMessage("badThrottleCommand",
1049                                i + 1, cmd.toString(), valType.toString());
1050                    }
1051                    break;
1052                case FORWARD:
1053                    if (valType != ValueType.VAL_TRUE && valType != ValueType.VAL_FALSE) {
1054                        return Bundle.getMessage("badThrottleCommand",
1055                                i + 1, cmd.toString(), valType.toString());
1056                    }
1057                    break;
1058                case FKEY:
1059                case LATCHF:
1060                    if (valType != ValueType.VAL_ON && valType != ValueType.VAL_OFF) {
1061                        return Bundle.getMessage("badThrottleCommand",
1062                                i + 1, cmd.toString(), valType.toString());
1063                    }
1064                    break;
1065                case SET_SENSOR:
1066                case WAIT_SENSOR:
1067                    if (valType != ValueType.VAL_ACTIVE && valType != ValueType.VAL_INACTIVE) {
1068                        return Bundle.getMessage("badThrottleCommand",
1069                                i + 1, cmd.toString(), valType.toString());
1070                    }
1071                    String msg = ts.getBeanDisplayName();
1072                    if (msg == null) {
1073                        return Bundle.getMessage("badThrottleCommand",
1074                                i + 1, cmd.toString(), valType.toString());
1075                    }
1076                    msg = WarrantFrame.checkBeanName(cmd, ts.getBeanDisplayName());
1077                    if (msg != null) {
1078                        return msg +
1079                                '\n' +
1080                                Bundle.getMessage("badThrottleCommand",
1081                                        i + 1, cmd.toString(), valType.toString());
1082                    }
1083                    break;
1084                case RUN_WARRANT:
1085                    if (valType != ValueType.VAL_INT) {
1086                        return Bundle.getMessage("badThrottleCommand",
1087                                i + 1, cmd.toString(), valType.toString());
1088                    }
1089                    msg = ts.getBeanDisplayName();
1090                    if (msg == null) {
1091                        return Bundle.getMessage("badThrottleCommand",
1092                                i + 1, cmd.toString(), valType.toString());
1093                    }
1094                    msg = WarrantFrame.checkBeanName(cmd, ts.getBeanDisplayName());
1095                    if (msg != null) {
1096                        return msg +
1097                                '\n' +
1098                                Bundle.getMessage("badThrottleCommand",
1099                                        i + 1, cmd.toString(), valType.toString());
1100                    }
1101                    break;
1102                case SPEEDSTEP:
1103                    if (valType != ValueType.VAL_STEP) {
1104                        return Bundle.getMessage("badThrottleCommand",
1105                                i + 1, cmd.toString(), valType.toString());
1106                    }
1107                    break;
1108                case SET_MEMORY:
1109                    if (valType != ValueType.VAL_TEXT) {
1110                        return Bundle.getMessage("badThrottleCommand",
1111                                i + 1, cmd.toString(), valType.toString());
1112                    }
1113                    msg = ts.getBeanDisplayName();
1114                    if (msg == null) {
1115                        return Bundle.getMessage("badThrottleCommand",
1116                                i + 1, cmd.toString(), valType.toString());
1117                    }
1118                    msg = WarrantFrame.checkBeanName(cmd, ts.getBeanDisplayName());
1119                    if (msg != null) {
1120                        return msg +
1121                                '\n' +
1122                                Bundle.getMessage("badThrottleCommand",
1123                                        i + 1, cmd.toString(), valType.toString());
1124                    }
1125                    break;
1126                default:
1127                    return Bundle.getMessage("BadThrottleSetting", i + 1);
1128            }
1129        }
1130        if (lastSpeed > 0.0f) {
1131            return Bundle.getMessage("BadLastSpeed", lastSpeed);
1132        }
1133        return null;
1134    }
1135
1136    static String checkBeanName(Command command, String beanName) {
1137        switch (command) {
1138            case SET_SENSOR:
1139            case WAIT_SENSOR:
1140                if (InstanceManager.sensorManagerInstance().getSensor(beanName) == null) {
1141                    return Bundle.getMessage("BadSensor", beanName);
1142                }
1143                break;
1144            case RUN_WARRANT:
1145                if (InstanceManager.getDefault(WarrantManager.class).getWarrant(beanName) == null) {
1146                    return Bundle.getMessage("BadWarrant", beanName);
1147                }
1148                break;
1149            case SET_MEMORY:
1150                if (InstanceManager.getDefault(jmri.MemoryManager.class).getMemory(beanName) == null) {
1151                    return Bundle.getMessage("BadMemory", beanName);
1152                }
1153                break;
1154            default:
1155                if (InstanceManager.getDefault(OBlockManager.class).getOBlock(beanName) == null) {
1156                    return Bundle.getMessage("BlockNotFound", beanName);
1157                }
1158                break;
1159        }
1160        return null;
1161    }
1162
1163    private void runLearnModeTrain() {
1164        _warrant.setSpeedUtil(_speedUtil); // transfer SpeedUtil to warrant
1165        String msg = null;
1166        if (isRunning()) {
1167            msg = Bundle.getMessage("CannotRun", _warrant.getDisplayName(),
1168                    Bundle.getMessage("TrainRunning", _warrant.getTrainName()));
1169        }
1170        if (msg == null) {
1171            _warrant.setBlockOrders(getOrders());
1172            msg = checkTrainId();
1173        }
1174        if (msg == null) {
1175            msg = _warrant.checkRoute();
1176        }
1177        if (msg == null) {
1178            msg = WarrantTableFrame.getDefault().getModel().checkAddressInUse(_warrant);
1179        }
1180        if (msg == null) {
1181            msg = _warrant.allocateRoute(false, getOrders());
1182        }
1183        toFront();
1184
1185        if (msg != null) {
1186            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("LearnError", msg),
1187                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
1188            _warrant.deAllocate();
1189            setStatus(msg, Color.red);
1190            return;
1191        }
1192
1193        if (!_throttleCommands.isEmpty()) {
1194            if (JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("deleteCommand"),
1195                    Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,
1196                    JmriJOptionPane.QUESTION_MESSAGE) != JmriJOptionPane.YES_OPTION ) {
1197                return;
1198            }
1199            _throttleCommands = new ArrayList<>();
1200            _commandModel.fireTableDataChanged();
1201        }
1202
1203        msg = _warrant.checkStartBlock();
1204        if (msg != null) {
1205            if (msg.equals("warnStart")) {
1206                msg = Bundle.getMessage("warnStart", getTrainName(), _warrant.getCurrentBlockName());
1207                JmriJOptionPane.showMessageDialog(this, msg,
1208                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
1209                setStatus(msg, Color.red);
1210                return;
1211            } else if (msg.equals("BlockDark")) {
1212                msg = Bundle.getMessage("BlockDark", _warrant.getCurrentBlockName(), getTrainName());
1213                if (JmriJOptionPane.YES_OPTION != JmriJOptionPane.showConfirmDialog(this,
1214                        Bundle.getMessage("OkToRun", msg), Bundle.getMessage("QuestionTitle"),
1215                        JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.WARNING_MESSAGE)) {
1216                    stopRunTrain(true);
1217                    setStatus(msg, Color.red);
1218                    return;
1219                }
1220            }
1221            setStatus(msg, Color.black);
1222        }
1223
1224        if (_learnThrottle == null) {
1225            _learnThrottle = new LearnThrottleFrame(this);
1226        } else {
1227            _learnThrottle.setVisible(true);
1228        }
1229
1230        _warrant.setTrainName(getTrainName());
1231        _startTime = System.currentTimeMillis();
1232        _speed = 0.0f;
1233
1234        _warrant.addPropertyChangeListener(this);
1235
1236        msg = _warrant.setRunMode(Warrant.MODE_LEARN, _speedUtil.getDccAddress(), _learnThrottle,
1237                _throttleCommands, _runETOnlyBox.isSelected());
1238        if (msg != null) {
1239            stopRunTrain(true);
1240            JmriJOptionPane.showMessageDialog(this, msg, Bundle.getMessage("WarningTitle"),
1241                    JmriJOptionPane.WARNING_MESSAGE);
1242            setStatus(msg, Color.red);
1243        }
1244    }
1245
1246    long lastClicktime; // keep double clicks from showing dialogs
1247    protected void runTrain() {
1248        long time = System.currentTimeMillis();
1249        if (time - lastClicktime < 1000) {
1250            return;
1251        }
1252        lastClicktime = time;
1253
1254        _warrant.setSpeedUtil(_speedUtil); // transfer SpeedUtil to warrant
1255        String msg = null;
1256        if (isRunning()) {
1257            msg = Bundle.getMessage("CannotRun", _warrant.getDisplayName(),
1258                    Bundle.getMessage("TrainRunning", _warrant.getTrainName()));
1259        }
1260        if (msg == null) {
1261            _warrant.setTrainName(getTrainName());
1262            _warrant.setShareRoute(_shareRouteBox.isSelected());
1263            _warrant.setAddTracker(_addTracker.isSelected());
1264            _warrant.setHaltStart(_haltStartBox.isSelected());
1265            _warrant.setNoRamp(_noRampBox.isSelected());
1266        }
1267        if (msg == null) {
1268            msg = checkTrainId();
1269        }
1270        if (msg == null) {
1271            msg = checkThrottleCommands();
1272            if (msg == null) {
1273                if (!_warrant.hasRouteSet() && _runETOnlyBox.isSelected()) {
1274                    msg = Bundle.getMessage("BlindRouteNotSet", _warrant.getDisplayName());
1275                }
1276            }
1277        }
1278        if (msg == null) {
1279            WarrantTableModel model = WarrantTableFrame.getDefault().getModel();
1280            msg = model.checkAddressInUse(_warrant);
1281        }
1282//        toFront();
1283        if (msg != null) {
1284            JmriJOptionPane.showMessageDialog(this, msg, Bundle.getMessage("WarningTitle"),
1285                    JmriJOptionPane.WARNING_MESSAGE);
1286//            _warrant.deAllocate();
1287            setStatus(msg, Color.black);
1288            return;
1289        }
1290        if (_warrant.getRunMode() != Warrant.MODE_NONE) {
1291            return;
1292        }
1293        _warrant.addPropertyChangeListener(this);
1294
1295        msg = _warrant.setRunMode(Warrant.MODE_RUN, _speedUtil.getDccAddress(), null,
1296                _throttleCommands, _runETOnlyBox.isSelected());
1297        if (msg != null) {
1298            clearWarrant();
1299            JmriJOptionPane.showMessageDialog(this, msg,
1300                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
1301            setStatus(msg, Color.red);
1302            return;
1303        }
1304
1305        msg = _warrant.checkStartBlock();
1306        if (msg != null) {
1307            if (msg.equals("warnStart")) {
1308                msg = Bundle.getMessage("warnStart", _warrant.getTrainName(), _warrant.getCurrentBlockName());
1309            } else if (msg.equals("BlockDark")) {
1310                msg = Bundle.getMessage("BlockDark", _warrant.getCurrentBlockName(), _warrant.getTrainName());
1311            }
1312            if (JmriJOptionPane.YES_OPTION != JmriJOptionPane.showConfirmDialog(this,
1313                    Bundle.getMessage("OkToRun", msg), Bundle.getMessage("QuestionTitle"),
1314                    JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.WARNING_MESSAGE)) {
1315                clearWarrant();
1316                setStatus(msg, Color.red);
1317            } else {
1318                setStatus(_warrant.getRunningMessage(), myGreen);
1319            }
1320        }
1321    }
1322
1323    /*
1324     * Stop a MODE_LEARN warrant, i.e. non-registered member _warrant
1325     */
1326    private void stopRunTrain(boolean aborted) {
1327        if (_learnThrottle != null) {
1328            _learnThrottle.dispose();
1329            _learnThrottle = null;
1330        }
1331        if (_warrant == null) {
1332            return;
1333        }
1334
1335        if (_warrant.getRunMode() == Warrant.MODE_LEARN) {
1336            List<BlockOrder> orders = getOrders();
1337            if (orders != null && orders.size() > 1) {
1338                BlockOrder bo = _warrant.getCurrentBlockOrder();
1339                if (bo != null) {
1340                    OBlock lastBlock = orders.get(orders.size() - 1).getBlock();
1341                    OBlock currentBlock = bo.getBlock();
1342                    if (!lastBlock.equals(currentBlock)) {
1343                        if ((lastBlock.getState() & OBlock.UNDETECTED) != 0 &&
1344                                currentBlock.equals(orders.get(orders.size() - 2).getBlock())) {
1345                            setThrottleCommand("NoOp", Bundle.getMessage("Mark"), lastBlock.getDisplayName());
1346                            setStatus(Bundle.getMessage("LearningStop"), myGreen);
1347                        } else if (!aborted) {
1348                            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("IncompleteScript", lastBlock),
1349                                    Bundle.getMessage("WarningTitle"),
1350                                    JmriJOptionPane.WARNING_MESSAGE);
1351                        }
1352                    } else {
1353                        setStatus(Bundle.getMessage("LearningStop"), myGreen);
1354                    }
1355                }
1356            }
1357        }
1358        clearWarrant();
1359    }
1360
1361    private void clearWarrant() {
1362        if (_warrant != null) {
1363            _warrant.stopWarrant(false, true);
1364            _warrant.removePropertyChangeListener(this);
1365        }
1366    }
1367
1368    protected Warrant getWarrant() {
1369        return _warrant;
1370    }
1371
1372    private void setStatus(String msg, Color c) {
1373        ThreadingUtil.runOnGUIEventually(() -> {
1374            _statusBox.setForeground(c);
1375            _statusBox.setText(msg);
1376        });
1377    }
1378
1379    @Override
1380    protected void maxThrottleEventAction() {
1381    }
1382
1383    /**
1384     * Property names from Warrant: "runMode" - from setRunMode "controlChange"
1385     * - from controlRunTrain "blockChange" - from goingActive "allocate" - from
1386     * allocateRoute, deAllocate "setRoute" - from setRoute, goingActive
1387     * Property names from Engineer: "Command" - from run "SpeedRestriction" -
1388     * ThrottleRamp run Property names from RouteFinder: "RouteSearch" - from
1389     * run
1390     */
1391    @Override
1392    public void propertyChange(java.beans.PropertyChangeEvent e) {
1393        String property = e.getPropertyName();
1394        if (property.equals("DnDrop")) {
1395            doAction(e.getSource());
1396        } else if (e.getSource() instanceof Warrant && _warrant.equals(e.getSource())) {
1397            if (log.isDebugEnabled())
1398                log.debug("propertyChange \"{}\" old= {} new= {} source= {}",
1399                        property, e.getOldValue(), e.getNewValue(), e.getSource().getClass().getName());
1400            String msg = null;
1401            Color color = myGreen;
1402            switch (_warrant.getRunMode()) {
1403                case Warrant.MODE_NONE:
1404                    _warrant.removePropertyChangeListener(this);
1405                    if (property.equals("StopWarrant")) {
1406                        String blkName = (String) e.getOldValue();
1407                        String bundleKey = (String) e.getNewValue();
1408                        if (blkName == null) {
1409                            msg = Bundle.getMessage(bundleKey,
1410                                    _warrant.getTrainName(), _warrant.getDisplayName());
1411                            color =  Color.red;
1412                        } else {
1413                            msg = Bundle.getMessage(bundleKey,
1414                                    _warrant.getTrainName(), _warrant.getDisplayName(),
1415                                    blkName);
1416                            color = myGreen;
1417                        }
1418                    }
1419                    break;
1420                case Warrant.MODE_LEARN:
1421                    if (property.equals("blockChange")) {
1422                        OBlock oldBlock = (OBlock) e.getOldValue();
1423                        OBlock newBlock = (OBlock) e.getNewValue();
1424                        if (newBlock == null) {
1425                            stopRunTrain(true);
1426                            msg = Bundle.getMessage("ChangedRoute",
1427                                    _warrant.getTrainName(),
1428                                    oldBlock.getDisplayName(),
1429                                    _warrant.getDisplayName());
1430                            color = Color.red;
1431                        } else {
1432                            setThrottleCommand("NoOp", Bundle.getMessage("Mark"),
1433                                    ((OBlock) e.getNewValue()).getDisplayName());
1434                            msg = Bundle.getMessage("TrackerBlockEnter",
1435                                    _warrant.getTrainName(),
1436                                    newBlock.getDisplayName());
1437                        }
1438                    } else if (property.equals("abortLearn")) {
1439                        stopRunTrain(true);
1440                        int oldIdx = ((Integer) e.getOldValue());
1441                        int newIdx = ((Integer) e.getNewValue());
1442                        if (oldIdx > newIdx) {
1443                            msg = Bundle.getMessage("LearnAbortOccupied",
1444                                    _warrant.getBlockAt(oldIdx),
1445                                    _warrant.getDisplayName());
1446                            color = Color.red;
1447                        } else {
1448                            msg = Bundle.getMessage("warrantAbort",
1449                                    _warrant.getTrainName(),
1450                                    _warrant.getDisplayName());
1451                            color = Color.red;
1452                        }
1453                    } else {
1454                        msg = Bundle.getMessage("Learning", _warrant.getCurrentBlockName());
1455                        color = Color.black;
1456                    }
1457                    break;
1458                case Warrant.MODE_RUN:
1459                case Warrant.MODE_MANUAL:
1460                    if (e.getPropertyName().equals("blockChange")) {
1461                        OBlock oldBlock = (OBlock) e.getOldValue();
1462                        OBlock newBlock = (OBlock) e.getNewValue();
1463                        if (newBlock == null) {
1464                            msg = Bundle.getMessage("ChangedRoute",
1465                                    _warrant.getTrainName(),
1466                                    oldBlock.getDisplayName(),
1467                                    _warrant.getDisplayName());
1468                            color = Color.red;
1469                        } else {
1470                            msg = Bundle.getMessage("TrackerBlockEnter",
1471                                    _warrant.getTrainName(),
1472                                    newBlock.getDisplayName());
1473                        }
1474                    } else if (e.getPropertyName().equals("ReadyToRun")) {
1475                        msg = _warrant.getRunningMessage();
1476                    } else if (e.getPropertyName().equals("SpeedChange")) {
1477                        msg = _warrant.getRunningMessage();
1478                        color = Color.black;
1479                    } else if (property.equals("SignalOverrun")) {
1480                        String name = (String) e.getOldValue();
1481                        String speed = (String) e.getNewValue();
1482                        msg = Bundle.getMessage("SignalOverrun",
1483                                _warrant.getTrainName(), speed, name);
1484                        color = Color.red;
1485                    } else if (property.equals("OccupyOverrun")) {
1486                        String blockName = (String) e.getOldValue();
1487                        OBlock occuppier = (OBlock) e.getNewValue();
1488                        msg = Bundle.getMessage("OccupyOverrun",
1489                                _warrant.getTrainName(), blockName, occuppier);
1490                        color = Color.red;
1491                    } else if (property.equals("WarrantOverrun")) {
1492                        String blkName = (String) e.getOldValue();
1493                        OBlock warName = (OBlock) e.getNewValue();
1494                        msg = Bundle.getMessage("WarrantOverrun",
1495                                _warrant.getTrainName(), blkName, warName);
1496                        color = Color.red;
1497                    } else if (e.getPropertyName().equals("WarrantStart")) {
1498                        msg = Bundle.getMessage("warrantStart",
1499                                _warrant.getTrainName(), _warrant.getDisplayName(),
1500                                _warrant.getCurrentBlockName());
1501                        if (_warrant.getState() == Warrant.HALT) {
1502                            JmriJOptionPane.showMessageDialog(this, _warrant.getRunningMessage(),
1503                                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
1504                        }
1505                    } else if (e.getPropertyName().equals("controlChange")) {
1506                        int newCntrl = ((Integer) e.getNewValue());
1507                        msg = Bundle.getMessage("controlChange",
1508                                _warrant.getTrainName(),
1509                                Bundle.getMessage(Warrant.CNTRL_CMDS[newCntrl]),
1510                                _warrant.getCurrentBlockName());
1511                        color = Color.black;
1512                    } else if (e.getPropertyName().equals("throttleFail")) {
1513                        msg = Bundle.getMessage("ThrottleFail",
1514                                _warrant.getTrainName(), e.getNewValue());
1515                        color = Color.red;
1516                    } else {
1517                        return;
1518                    }
1519                    break;
1520                default:
1521            }
1522            setStatus(msg, color);
1523        }
1524        invalidate();
1525    }
1526
1527    protected void setThrottleCommand(String cmd, String value) {
1528        String bName = Bundle.getMessage("NoBlock");
1529        BlockOrder bo = _warrant.getCurrentBlockOrder();
1530        if (bo != null) {
1531            OBlock block = bo.getBlock();
1532            if (block != null) {
1533                bName = block.getDisplayName();
1534            }
1535        }
1536        /*
1537         * if (cmd.equals("Forward")) {
1538         * _speedUtil.setIsForward(Boolean.parseBoolean(value)); }
1539         */
1540        setThrottleCommand(cmd, value, bName);
1541    }
1542
1543    protected void setSpeedCommand(float speed) {
1544        if (_warrant.getSpeedUtil().profileHasSpeedInfo()) {
1545            _speed = _warrant.getSpeedUtil().getTrackSpeed(speed); // mm/ms
1546        } else {
1547            _speed = 0.0f;
1548        }
1549        setThrottleCommand("speed", Float.toString(speed));
1550    }
1551
1552    private void setThrottleCommand(String cmd, String value, String bName) {
1553        long endTime = System.currentTimeMillis();
1554        long time = endTime - _startTime;
1555        _startTime = endTime;
1556        ThrottleSetting ts = new ThrottleSetting(time, cmd, value, bName, _speed);
1557        if (log.isDebugEnabled()) {
1558            log.debug("setThrottleCommand= {}", ts.toString());
1559        }
1560        _throttleCommands.add(ts);
1561        _commandModel.fireTableDataChanged();
1562
1563        scrollCommandTable(_commandModel.getRowCount());
1564    }
1565
1566    private void scrollCommandTable(int row) {
1567        JScrollBar bar = _throttlePane.getVerticalScrollBar();
1568        bar.setValue(row * _rowHeight);
1569        bar.invalidate();
1570    }
1571
1572    /**
1573     * Called by WarrantTableAction before closing the editing of this warrant
1574     *
1575     * @return true if this warrant or its pre-editing version is running
1576     */
1577    protected boolean isRunning() {
1578        return _warrant._runMode != Warrant.MODE_NONE ||
1579            (_saveWarrant != null && _saveWarrant._runMode != Warrant.MODE_NONE);
1580    }
1581
1582    /**
1583     * Verify that commands are correct
1584     *
1585     * @return true if commands are OK
1586     */
1587    private boolean save() {
1588        boolean fatal = false;
1589        String msg = null;
1590        if (isRunning()) {
1591            msg = Bundle.getMessage("CannotEdit", _warrant.getDisplayName());
1592        }
1593        if (msg == null) {
1594            msg = routeIsValid();
1595        }
1596        if (msg != null) {
1597            msg = Bundle.getMessage("SaveError", msg);
1598            fatal = true;
1599        }
1600        if (msg == null) {
1601            msg = checkLocoAddress();
1602        }
1603        if (msg == null && !_isSCWarrant.isSelected()) {
1604            msg = checkThrottleCommands();
1605            if (msg != null) {
1606                msg = Bundle.getMessage("BadData", msg);
1607                fatal = true;
1608            }
1609        }
1610
1611        WarrantManager mgr = InstanceManager.getDefault(WarrantManager.class);
1612        if (msg == null) {
1613            if (_saveWarrant != null) {
1614                if ((_saveWarrant instanceof SCWarrant && !_isSCWarrant.isSelected()) ||
1615                        (!(_saveWarrant instanceof SCWarrant) && _isSCWarrant.isSelected())) {
1616                    // _saveWarrant already registered, but is not the correct
1617                    // class.
1618                    mgr.deregister(_saveWarrant);
1619                    _warrant = mgr.createNewWarrant(
1620                            _sysNameBox.getText(), _userNameBox.getText(), _isSCWarrant.isSelected(),
1621                            (long) _TTPtextField.getValue());
1622                } else {
1623                    String uName = _userNameBox.getText();
1624                    if (uName.length() > 0 &&
1625                            !uName.equals(_saveWarrant.getUserName()) &&
1626                            mgr.getWarrant(uName) != null) {
1627                        fatal = true;
1628                        msg = Bundle.getMessage("WarrantExists", _userNameBox.getText());
1629                    } else {
1630                        _warrant = _saveWarrant; // update registered warrant
1631                    }
1632                }
1633            } else {
1634                if (_warrant == null) {
1635                    _warrant = mgr.createNewWarrant(
1636                            _sysNameBox.getText(), _userNameBox.getText(),
1637                            _isSCWarrant.isSelected(), (long) _TTPtextField.getValue());
1638                }
1639            }
1640        }
1641        if (_warrant == null) { // find out why
1642            if (_userNameBox.getText().length() > 0 && mgr.getByUserName(_userNameBox.getText()) != null) {
1643                msg = Bundle.getMessage("WarrantExists", _userNameBox.getText());
1644            } else if (mgr.getBySystemName(_sysNameBox.getText()) != null) {
1645                msg = Bundle.getMessage("WarrantExists", _sysNameBox.getText());
1646            } else {
1647                msg = Bundle.getMessage("IWSystemName", _sysNameBox.getText());
1648            }
1649            fatal = true;
1650        }
1651        if (msg == null && _userNameBox.getText().length() == 0) {
1652            msg = Bundle.getMessage("NoUserName", _sysNameBox.getText());
1653        }
1654        if (msg != null) {
1655            if (fatal) {
1656                JmriJOptionPane.showMessageDialog(this, msg,
1657                        Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
1658                return false;
1659            }
1660            int result = JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("SaveQuestion", msg),
1661                    Bundle.getMessage("QuestionTitle"),
1662                    JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE);
1663            if (result != JmriJOptionPane.YES_OPTION ) {
1664                if (_warrant != null) {
1665                    mgr.deregister(_warrant);
1666                }
1667                return false;
1668            }
1669        }
1670
1671        if (_isSCWarrant.isSelected()) {
1672            ((SCWarrant) _warrant).setForward(_runForward.isSelected());
1673            ((SCWarrant) _warrant).setTimeToPlatform((long) _TTPtextField.getValue());
1674            long sf = (long) _speedFactorTextField.getValue();
1675            float sf_float = sf;
1676            ((SCWarrant) _warrant).setSpeedFactor(sf_float / 100);
1677        }
1678        _warrant.setTrainName(getTrainName());
1679        _warrant.setRunBlind(_runETOnlyBox.isSelected());
1680        _warrant.setShareRoute(_shareRouteBox.isSelected());
1681        _warrant.setAddTracker(_addTracker.isSelected());
1682        _warrant.setNoRamp(_noRampBox.isSelected());
1683        _warrant.setHaltStart(_haltStartBox.isSelected());
1684        _warrant.setUserName(_userNameBox.getText());
1685
1686        _warrant.setViaOrder(getViaBlockOrder());
1687        _warrant.setAvoidOrder(getAvoidBlockOrder());
1688        _warrant.setBlockOrders(getOrders());
1689        _warrant.setThrottleCommands(_throttleCommands);
1690        _warrant.setSpeedUtil(_speedUtil); // transfer SpeedUtil to warrant
1691        if (_saveWarrant == null) {
1692            try {
1693                mgr.register(_warrant);
1694            } catch (jmri.NamedBean.DuplicateSystemNameException dsne) {
1695                // ignore
1696            }
1697            _saveWarrant = _warrant;
1698        }
1699
1700        if (log.isDebugEnabled())
1701            log.debug("warrant {} saved _train {} name= {}",
1702                    _warrant.getDisplayName(), _speedUtil.getRosterId(), getTrainName());
1703        WarrantTableAction.getDefault().updateWarrantMenu();
1704        WarrantTableFrame.getDefault().getModel().fireTableDataChanged();
1705        _dirty = false;
1706        return true;
1707    }
1708
1709    protected List<ThrottleSetting> getThrottleCommands() {
1710        return _throttleCommands;
1711    }
1712
1713    protected void close() {
1714        _dirty = false;
1715        clearTempWarrant();
1716        if (_warrant.getRunMode() != Warrant.MODE_NONE) {
1717            stopRunTrain(true);
1718        }
1719        closeProfileTable();
1720        dispose();
1721    }
1722
1723    // =============== Throttle Command Table ==========================\\
1724    // =============== VALUE_COLUMN editing/rendering ==================\\
1725
1726    static String[] TRUE_FALSE = {ValueType.VAL_TRUE.toString(), ValueType.VAL_FALSE.toString()};
1727    static String[] ON_OFF = {ValueType.VAL_ON.toString(), ValueType.VAL_OFF.toString()};
1728    static String[] SENSOR_STATES = {ValueType.VAL_ACTIVE.toString(), ValueType.VAL_INACTIVE.toString()};
1729
1730    class ValueCellEditor extends DefaultCellEditor {
1731
1732        private ComboDialog editorDialog;
1733        private TextDialog textDialog;
1734        private String currentText;
1735
1736        ValueCellEditor(JTextField textField) {
1737            super(textField);
1738            setClickCountToStart(1);
1739            log.debug("valueCellEditor Ctor");
1740        }
1741
1742        @Override
1743        public Component getTableCellEditorComponent(JTable table, Object value,
1744                boolean isSelected, int row, int col) {
1745            if (log.isDebugEnabled()) {
1746                log.debug("getValueCellEditorComponent: row= {}, column= {} selected = {} value= {}",
1747                        row, col, isSelected, value);
1748            }
1749            currentText = value.toString();
1750            editorComponent = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, row, col);
1751            Command cmd = (Command) _commandModel.getValueAt(row, ThrottleTableModel.COMMAND_COLUMN);
1752            Rectangle cellRect = table.getCellRect(row, col, false);
1753            Dimension dim = new Dimension(cellRect.width, cellRect.height);
1754
1755            if (cmd == null) {
1756                showTextDialog(dim);
1757            } else {
1758                switch (cmd) {
1759                    case FORWARD:
1760                        showComboDialog(TRUE_FALSE, dim);
1761                        break;
1762                    case FKEY:
1763                    case LATCHF:
1764                        showComboDialog(ON_OFF, dim);
1765                        break;
1766                    case SET_SENSOR:
1767                    case WAIT_SENSOR:
1768                        showComboDialog(SENSOR_STATES, dim);
1769                        break;
1770                    default:
1771                        // includes cases SPEED: and RUN_WARRANT:
1772                        // SPEEDSTEP and NOOP not included in ComboBox
1773                        showTextDialog(dim);
1774                        break;
1775                }
1776            }
1777            return editorComponent;
1778        }
1779
1780        void showTextDialog(Dimension dim) {
1781            log.debug("valueCellEditor.showTextDialog");
1782            textDialog = new TextDialog();
1783            textDialog._textField.setText(currentText);
1784
1785            class CellMaker implements Runnable {
1786                Dimension dim;
1787
1788                CellMaker(Dimension d) {
1789                    dim = d;
1790                }
1791
1792                @Override
1793                public void run() {
1794                    log.debug("Run valueCellEditor.TextDialog");
1795                    Point p = editorComponent.getLocationOnScreen();
1796                    textDialog.setLocation(p.x, p.y);
1797                    textDialog.setPreferredSize(dim);
1798                    textDialog.pack();
1799                    textDialog.setVisible(true);
1800                }
1801            }
1802            CellMaker t = new CellMaker(dim);
1803            javax.swing.SwingUtilities.invokeLater(t);
1804        }
1805
1806        class TextDialog extends JDialog implements FocusListener {
1807            JTextField _textField;
1808            TextDialog _this;
1809
1810            TextDialog() {
1811                super((JFrame) null, false);
1812                _this = this;
1813                _textField = new JTextField();
1814                _textField.addFocusListener(TextDialog.this);
1815                _textField.setForeground(Color.RED);
1816                getContentPane().add(_textField);
1817                setUndecorated(true);
1818            }
1819
1820            @Override
1821            public void focusGained(FocusEvent e) {
1822            }
1823
1824            @Override
1825            public void focusLost(FocusEvent e) {
1826                currentText = _textField.getText();
1827                ((JTextField)editorComponent).setText(currentText);
1828                fireEditingStopped();
1829                _this.dispose();
1830            }
1831        }
1832
1833        void showComboDialog(String[] items, Dimension dim) {
1834            editorDialog = new ComboDialog(items);
1835            log.debug("valueCellEditor.showComboDialog");
1836
1837            class CellMaker implements Runnable {
1838                Dimension dim;
1839
1840                CellMaker(Dimension d) {
1841                    dim = d;
1842                }
1843
1844                @Override
1845                public void run() {
1846                    log.debug("Run valueCellEditor.showDialog");
1847                    Point p = editorComponent.getLocationOnScreen();
1848                    editorDialog.setLocation(p.x, p.y);
1849                    editorDialog.setPreferredSize(dim);
1850                    editorDialog.pack();
1851                    editorDialog.setVisible(true);
1852                }
1853            }
1854            CellMaker t = new CellMaker(dim);
1855            javax.swing.SwingUtilities.invokeLater(t);
1856        }
1857
1858        class ComboDialog extends JDialog implements ItemListener, FocusListener {
1859            JComboBox<String> _comboBox;
1860            ComboDialog _this;
1861
1862            ComboDialog(String[] items) {
1863                super((JFrame) null, false);
1864                _this = this;
1865                _comboBox = new JComboBox<>();
1866                _comboBox.addItemListener(ComboDialog.this);
1867                _comboBox.addFocusListener(ComboDialog.this);
1868                _comboBox.setForeground(Color.RED);
1869                for (String item : items) {
1870                    _comboBox.addItem(item);
1871                }
1872                _comboBox.removeItem(Command.NOOP.toString());
1873                getContentPane().add(_comboBox);
1874                setUndecorated(true);
1875            }
1876
1877            @Override
1878            public void itemStateChanged(ItemEvent e) {
1879                currentText = (String) _comboBox.getSelectedItem();
1880                ((JTextField)editorComponent).setText(currentText);
1881                fireEditingStopped();
1882                _this.dispose();
1883            }
1884
1885            @Override
1886            public void focusGained(FocusEvent e) {
1887            }
1888
1889            @Override
1890            public void focusLost(FocusEvent e) {
1891                currentText = (String) _comboBox.getSelectedItem();
1892                ((JTextField)editorComponent).setText(currentText);
1893                fireEditingStopped();
1894                _this.dispose();
1895            }
1896        }
1897    }
1898
1899    // =============== COMMAND_COLUMN editing/rendering ===============\\
1900
1901    class CommandCellEditor extends DefaultCellEditor {
1902        CommandCellEditor(JComboBox<Command> comboBox) {
1903            super(comboBox);
1904            log.debug("New JComboBox<String> CommandCellEditor");
1905        }
1906
1907        @SuppressWarnings("unchecked") // getComponent call requires an
1908                                       // unchecked cast
1909        @Override
1910        public Component getTableCellEditorComponent(JTable table, Object value,
1911                boolean isSelected, int row, int column) {
1912            if (log.isDebugEnabled()) {
1913                log.debug("getTableCellEditorComponent: row= {}, column= {} selected = {}",
1914                        row, column, isSelected);
1915            }
1916            JComboBox<Command> comboBox = (JComboBox<Command>) getComponent();
1917            cellPt = MouseInfo.getPointerInfo().getLocation();
1918            comboBox.removeAllItems();
1919            for (Command cmd : Command.values()) {
1920                if (!cmd.name().equals("NOOP") && !cmd.name().equals("SPEEDSTEP")) {
1921                    comboBox.addItem(cmd);
1922                }
1923            }
1924            return super.getTableCellEditorComponent(table, value, isSelected, row, column);
1925        }
1926    }
1927
1928    Point cellPt; // point to display key
1929
1930    class CommandCellRenderer extends DefaultTableCellRenderer {
1931        public CommandCellRenderer() {
1932            super();
1933            log.debug("New JComboBox<String> CommandCellRenderer");
1934        }
1935
1936        @Override
1937        public Component getTableCellRendererComponent(JTable table, Object value,
1938                boolean isSelected, boolean hasFocus, int row, int column) {
1939            Command cmd = (Command) value;
1940            int key = _throttleCommands.get(row).getKeyNum();
1941            if (null == cmd) {
1942                setText(null);
1943            } else switch (cmd) {
1944                case FKEY:
1945                    setText(Bundle.getMessage("FKey", key));
1946                    break;
1947                case LATCHF:
1948                    setText(Bundle.getMessage("FKeyMomemtary", key));
1949                    break;
1950                default:
1951                    setText(cmd.toString());
1952                    break;
1953            }
1954            return this;
1955        }
1956    }
1957
1958    static class EditDialog extends JDialog {
1959        SpinnerNumberModel _keyNumModel;
1960        ThrottleSetting _ts;
1961        Command _cmd;
1962
1963        EditDialog(JFrame frame, ThrottleSetting ts, Command cmd) {
1964            super(frame, true);
1965            _ts = ts;
1966            _cmd = cmd;
1967            int key = ts.getKeyNum();
1968            if (key < 0) {
1969                key = 0;
1970            }
1971            _keyNumModel = new SpinnerNumberModel(key, 0, 28, 1);
1972            JSpinner keyNums = new JSpinner(_keyNumModel);
1973            JPanel panel = new JPanel();
1974            panel.setLayout(new BorderLayout());
1975            panel.add(new JLabel(Bundle.getMessage("editFunctionKey")), BorderLayout.NORTH);
1976            panel.add(keyNums, BorderLayout.CENTER);
1977
1978            JPanel p = new JPanel();
1979            p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
1980            JButton doneButton;
1981            doneButton = new JButton(Bundle.getMessage("ButtonDone"));
1982            doneButton.addActionListener((ActionEvent a) -> done());
1983            p.add(doneButton);
1984
1985            JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
1986            cancelButton.addActionListener((ActionEvent a) -> this.dispose());
1987            p.add(cancelButton);
1988            panel.add(p, BorderLayout.SOUTH);
1989            getContentPane().add(panel);
1990            setUndecorated(true);
1991        }
1992
1993        public void done() {
1994            int i = (Integer) _keyNumModel.getValue();
1995            _ts.setKeyNum(i);
1996            _ts.setCommand(_cmd);
1997            this.dispose();
1998        }
1999
2000    }
2001
2002    void makeEditWindow(ThrottleSetting ts, Command cmd) {
2003        JDialog dialog = new EditDialog(this, ts, cmd);
2004        dialog.setLocation(cellPt);
2005        dialog.pack();
2006        dialog.setVisible(true);
2007        if (log.isDebugEnabled()) {
2008            log.debug("makeEditWindow: pt at ({}, {})", cellPt.x, cellPt.y);
2009        }
2010    }
2011
2012    static java.text.DecimalFormat twoDigit = new java.text.DecimalFormat("0.00");
2013
2014    /************************* Throttle Table ******************************/
2015    class ThrottleTableModel extends AbstractTableModel {
2016
2017        public static final int ROW_NUM = 0;
2018        public static final int TIME_COLUMN = 1;
2019        public static final int COMMAND_COLUMN = 2;
2020        public static final int VALUE_COLUMN = 3;
2021        public static final int BLOCK_COLUMN = 4;
2022        public static final int SPEED_COLUMN = 5;
2023        public static final int NUMCOLS = 6;
2024
2025        JComboBox<Integer> keyNums = new JComboBox<>();
2026
2027        public ThrottleTableModel() {
2028            super();
2029            for (int i = 0; i < 29; i++) {
2030                keyNums.addItem(i);
2031            }
2032        }
2033
2034        @Override
2035        public int getColumnCount() {
2036            return NUMCOLS;
2037        }
2038
2039        @Override
2040        public int getRowCount() {
2041            return _throttleCommands.size();
2042        }
2043
2044        @Override
2045        public String getColumnName(int col) {
2046            switch (col) {
2047                case ROW_NUM:
2048                    return "#";
2049                case TIME_COLUMN:
2050                    return Bundle.getMessage("TimeCol");
2051                case COMMAND_COLUMN:
2052                    return Bundle.getMessage("CommandCol");
2053                case VALUE_COLUMN:
2054                    return Bundle.getMessage("ValueCol");
2055                case BLOCK_COLUMN:
2056                    return Bundle.getMessage("BlockCol");
2057                case SPEED_COLUMN:
2058                    return Bundle.getMessage("trackSpeed");
2059                default:
2060                    // fall through
2061                    break;
2062            }
2063            return "";
2064        }
2065
2066        @Override
2067        public boolean isCellEditable(int row, int col) {
2068            return !(col == ROW_NUM || col == SPEED_COLUMN);
2069        }
2070
2071        @Override
2072        public Class<?> getColumnClass(int col) {
2073            if (col == COMMAND_COLUMN) {
2074                return JComboBox.class;
2075            }
2076            return String.class;
2077        }
2078
2079        public int getPreferredWidth(int col) {
2080            switch (col) {
2081                case ROW_NUM:
2082                    return new JTextField(3).getPreferredSize().width;
2083                case TIME_COLUMN:
2084                    return new JTextField(8).getPreferredSize().width;
2085                case COMMAND_COLUMN:
2086                case VALUE_COLUMN:
2087                    return new JTextField(18).getPreferredSize().width;
2088                case BLOCK_COLUMN:
2089                    return new JTextField(45).getPreferredSize().width;
2090                case SPEED_COLUMN:
2091                    return new JTextField(10).getPreferredSize().width;
2092                default:
2093                    return new JTextField(12).getPreferredSize().width;
2094            }
2095        }
2096
2097        @Override
2098        public Object getValueAt(int row, int col) {
2099            // some error checking
2100            if (row >= _throttleCommands.size()) {
2101                if (log.isDebugEnabled())
2102                    log.debug("row {} is greater than throttle command size {}",
2103                            row, _throttleCommands.size());
2104                return "";
2105            }
2106            ThrottleSetting ts = _throttleCommands.get(row);
2107            if (ts == null) {
2108                if (log.isDebugEnabled())
2109                    log.debug("Throttle setting is null!");
2110                return "";
2111            }
2112            switch (col) {
2113                case ROW_NUM:
2114                    return row + 1;
2115                case TIME_COLUMN:
2116                    return ts.getTime();
2117                case COMMAND_COLUMN:
2118                    return ts.getCommand();
2119                case VALUE_COLUMN:
2120                    CommandValue cmdVal = ts.getValue();
2121                    if (cmdVal == null) {
2122                        return "";
2123                    }
2124                    return cmdVal.showValue();
2125                case BLOCK_COLUMN:
2126                    return ts.getBeanDisplayName();
2127                case SPEED_COLUMN:
2128                    return twoDigit.format(ts.getTrackSpeed() * _speedConversion);
2129                default:
2130                    return "";
2131            }
2132        }
2133
2134        @Override
2135        @SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
2136                justification = "put least likely cases last for efficiency")
2137        public void setValueAt(Object value, int row, int col) {
2138            if (row >= _throttleCommands.size()) {
2139                return;
2140            }
2141            ThrottleSetting ts = _throttleCommands.get(row);
2142            String msg = null;
2143            switch (col) {
2144                case TIME_COLUMN:
2145                    try {
2146                        long time = Long.parseLong((String) value);
2147                        if (time < 0) {
2148                            msg = Bundle.getMessage("InvalidTime", (String) value);
2149                        } else {
2150                            ts.setTime(time);
2151                            _dirty = true;
2152                        }
2153                    } catch (NumberFormatException nfe) {
2154                        msg = Bundle.getMessage("InvalidTime", (String) value);
2155                    }
2156                    break;
2157                case COMMAND_COLUMN:
2158                    Command cmd = ((Command) value);
2159                    if (cmd == null) {
2160                        break;
2161                    }
2162                    Command prCmd = ts.getCommand();
2163                    if (prCmd != null) {
2164                        if (prCmd.equals(Command.NOOP)) {
2165                            break;
2166                        }
2167                        if (!cmd.hasBlockName() && prCmd.hasBlockName()) {
2168                            ts.setNamedBeanHandle(null);
2169                        }
2170                    }
2171                    switch (cmd) {
2172                        case FKEY:
2173                        case LATCHF:
2174                            class CellMaker implements Runnable {
2175                                ThrottleSetting ts;
2176                                Command cmd;
2177
2178                                CellMaker(ThrottleSetting t, Command c) {
2179                                    ts = t;
2180                                    cmd = c;
2181                                }
2182
2183                                @Override
2184                                public void run() {
2185                                    makeEditWindow(ts, cmd);
2186                                }
2187                            }
2188                            CellMaker t = new CellMaker(ts, cmd);
2189                            javax.swing.SwingUtilities.invokeLater(t);
2190                            break;
2191                        case NOOP:
2192                            msg = Bundle.getMessage("cannotEnterNoop", cmd.toString());
2193                            break;
2194                        case SPEED:
2195                        case FORWARD:
2196                        case SET_SENSOR:
2197                        case WAIT_SENSOR:
2198                        case RUN_WARRANT:
2199                        case SPEEDSTEP:
2200                        case SET_MEMORY:
2201                            ts.setCommand(cmd);
2202                            _dirty = true;
2203                            break;
2204                        default:
2205                            msg = Bundle.getMessage("badCommand", cmd.toString());
2206                    }
2207                    break;
2208                case VALUE_COLUMN:
2209                    if (value == null || ((String) value).length() == 0) {
2210                        break;
2211                    }
2212                    if (ts == null || ts.getCommand() == null) {
2213                        msg = Bundle.getMessage("nullValue", Bundle.getMessage("CommandCol"));
2214                        break;
2215                    }
2216                    Command command = ts.getCommand();
2217                    if (command.equals(Command.NOOP)) {
2218                        break;
2219                    }
2220                    try {
2221                        CommandValue val = ThrottleSetting.getValueFromString(command, (String) value);
2222                        if (!val.equals(ts.getValue())) {
2223                            _dirty = true;
2224                            ts.setValue(val);
2225                        }
2226                    } catch (jmri.JmriException je) {
2227                        msg = je.getMessage();
2228                        break;
2229                    }
2230                    if (command.hasBlockName()) {
2231                        NamedBeanHandle<?> bh = getPreviousBlockHandle(row);
2232                        ts.setNamedBeanHandle(bh);
2233                    }
2234                    break;
2235                case BLOCK_COLUMN:
2236                    if (ts == null || ts.getCommand() == null) {
2237                        msg = Bundle.getMessage("nullValue", Bundle.getMessage("CommandCol"));
2238                        break;
2239                    }
2240                    command = ts.getCommand();
2241                    if (command == null) {
2242                        break;
2243                    }
2244                    if (!command.hasBlockName()) {
2245                        msg = ts.setNamedBean(command, (String) value);
2246                    } else if (command.equals(Command.NOOP)) {
2247                        if (!((String) value).equals(ts.getBeanDisplayName())) {
2248                            msg = Bundle.getMessage("cannotChangeBlock", (String) value);
2249                        }
2250                    } else {
2251                        NamedBeanHandle<?> bh = getPreviousBlockHandle(row);
2252                        if (bh != null) {
2253                            String name = bh.getBean().getDisplayName();
2254                            if (!name.equals(value)) {
2255                                msg = Bundle.getMessage("commandInBlock", name);
2256                                ts.setNamedBeanHandle(bh);
2257                                _dirty = true;
2258                            }
2259                        }
2260                    }
2261                    break;
2262                case SPEED_COLUMN:
2263                    break;
2264                default:
2265            }
2266            if (msg != null) {
2267                showWarning(msg);
2268            } else {
2269                fireTableRowsUpdated(row, row);
2270            }
2271        }
2272
2273        private NamedBeanHandle<? extends NamedBean> getPreviousBlockHandle(int row) {
2274            for (int i = row; i > 0; i--) {
2275                NamedBeanHandle<? extends NamedBean> bh = _throttleCommands.get(i - 1).getNamedBeanHandle();
2276                if (bh != null && (bh.getBean() instanceof OBlock)) {
2277                    return bh;
2278                }
2279            }
2280            return null;
2281        }
2282
2283    }
2284
2285    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WarrantFrame.class);
2286
2287}