001package jmri.jmrit.dispatcher;
002
003import java.awt.BorderLayout;
004import java.awt.Dimension;
005import java.awt.FlowLayout;
006import java.awt.event.ActionEvent;
007import java.util.ArrayList;
008
009import javax.swing.BoxLayout;
010import javax.swing.ButtonGroup;
011import javax.swing.JButton;
012import javax.swing.JCheckBoxMenuItem;
013import javax.swing.JLabel;
014import javax.swing.JMenu;
015import javax.swing.JMenuBar;
016import javax.swing.JPanel;
017import javax.swing.JRadioButton;
018import javax.swing.JScrollPane;
019import javax.swing.JSeparator;
020import javax.swing.JSlider;
021import javax.swing.JTextField;
022
023import java.beans.PropertyChangeEvent;
024
025import javax.swing.JToolBar;
026import javax.swing.event.AncestorEvent;
027import javax.swing.event.AncestorListener;
028import javax.swing.plaf.basic.BasicToolBarUI;
029
030import jmri.Block;
031import jmri.Throttle;
032import jmri.jmrit.roster.RosterEntry;
033import jmri.util.JmriJFrame;
034import jmri.util.swing.JmriJOptionPane;
035
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039/**
040 * AutoTrainsFrame provides a user interface to trains that are running
041 * automatically under Dispatcher.
042 * <p>
043 * There is only one AutoTrains window. AutoTrains are added and deleted from
044 * this window as they are added or terminated.
045 * <p>
046 * This file is part of JMRI.
047 * <p>
048 * JMRI is open source software; you can redistribute it and/or modify it under
049 * the terms of version 2 of the GNU General Public License as published by the
050 * Free Software Foundation. See the "COPYING" file for a copy of this license.
051 * <p>
052 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
053 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
054 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
055 *
056 * @author Dave Duchamp Copyright (C) 2010
057 */
058public class AutoTrainsFrame extends jmri.util.JmriJFrame {
059
060    public AutoTrainsFrame(DispatcherFrame disp) {
061        super(false, true);
062        initializeAutoTrainsWindow();
063    }
064
065    // instance variables
066    private final ArrayList<AutoActiveTrain> _autoTrainsList = new ArrayList<>();
067    //Keep track of throttle and listeners to update frame with their current state.
068
069    // accessor functions
070    public ArrayList<AutoActiveTrain> getAutoTrainsList() {
071        return _autoTrainsList;
072    }
073
074    /**
075     * Creates and initializes a new control of type AutoTrainControl
076     * @param autoActiveTrain the new train.
077     */
078    public void addAutoActiveTrain(AutoActiveTrain autoActiveTrain) {
079        if (autoActiveTrain != null) {
080            log.debug("Adding ActiveTrain[{}]",autoActiveTrain.getActiveTrain().getActiveTrainName());
081            AutoTrainControl atn = new AutoTrainControl(autoActiveTrain);
082            if (!trainsCanBeFloated.isSelected()) {
083                atn.componentJPanel.setFloatable(false);
084            }
085            trainsPanel.add(atn);
086            atn.addPropertyChangeListener("terminated", (PropertyChangeEvent e) -> {
087                AutoTrainControl atnn = (AutoTrainControl) e.getSource();
088                // must be attached to make it really go away
089                ((BasicToolBarUI) atnn.componentJPanel.getUI()).setFloating(false,null);
090                trainsPanel.remove((AutoTrainControl) e.getSource());
091                pack();
092            });
093            // bit of overkill for when a floater floats and comes back.
094            atn.componentJPanel.addAncestorListener ( new AncestorListener ()
095            {
096                @Override
097                public void ancestorAdded ( AncestorEvent event )
098                {
099                    log.trace("ancestorAdded");
100                    pack();
101                }
102                @Override
103                public void ancestorRemoved ( AncestorEvent event )
104                {
105                    log.trace("ancestorRemoved");
106                    pack();
107                }
108                @Override
109                public void ancestorMoved ( AncestorEvent event )
110                {
111                    // blank.
112                }
113              } );
114            pack();
115        }
116    }
117
118    // variables for AutoTrains window
119    protected JmriJFrame autoTrainsFrame = null;
120    private JPanel trainsPanel;
121    private JScrollPane trainScrollPanel;
122    private JCheckBoxMenuItem frameHasScrollBars = new JCheckBoxMenuItem(Bundle.getMessage("AutoTrainsFrameUseScrollBars"));
123    private JCheckBoxMenuItem trainsCanBeFloated = new JCheckBoxMenuItem(Bundle.getMessage("AutoTrainsFrameAllowFloat"));
124    private JCheckBoxMenuItem frameAlwaysOnTop = new JCheckBoxMenuItem(Bundle.getMessage("AutoTrainsFrameAlwaysOnTop"));
125    private JCheckBoxMenuItem frameOnTopOnSpeedChange = new JCheckBoxMenuItem(Bundle.getMessage("AutoTrainsFrameOnTopOnSpeedChange"));
126
127    jmri.UserPreferencesManager prefMan;
128
129    private void initializeAutoTrainsWindow() {
130
131        prefMan = jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class);
132        frameHasScrollBars.setSelected(prefMan.getSimplePreferenceState(hasScrollBars));
133        trainsCanBeFloated.setSelected(prefMan.getSimplePreferenceState(canFloat));
134        frameAlwaysOnTop.setSelected(prefMan.getSimplePreferenceState(alWaysOnTop));
135        frameOnTopOnSpeedChange.setSelected(prefMan.getSimplePreferenceState(onTopOnSpeedChange));
136
137
138        autoTrainsFrame = this;
139        autoTrainsFrame.setTitle(Bundle.getMessage("TitleAutoTrains"));
140        trainsPanel = new JPanel();
141        trainsPanel.setLayout(new BoxLayout(trainsPanel, BoxLayout.Y_AXIS));
142        JMenuBar menuBar = new JMenuBar();
143        JMenu optMenu = new JMenu(Bundle.getMessage("MenuOptions")); // NOI18N
144        optMenu.add(frameHasScrollBars);
145        frameHasScrollBars.addActionListener(e -> {
146            setScrollBars();
147        });
148
149        optMenu.add(trainsCanBeFloated);
150        trainsCanBeFloated.addActionListener(e -> {
151            for (Object ob : trainsPanel.getComponents()) {
152                if (ob instanceof AutoTrainControl) {
153                    AutoTrainControl atnn = (AutoTrainControl) ob;
154                    if (trainsCanBeFloated.isSelected()) {
155                        atnn.componentJPanel.setFloatable(true);
156                    } else {
157                        // rejoin floating throttles before banning
158                        // floating.
159                        ((BasicToolBarUI) atnn.componentJPanel.getUI()).setFloating(false, null);
160                        atnn.componentJPanel.setFloatable(false);
161                    }
162                }
163            }
164        });
165
166        optMenu.add(frameAlwaysOnTop);
167        frameAlwaysOnTop.addActionListener(e -> {
168            setAlwaysOnTop(frameAlwaysOnTop.isSelected());
169        });
170
171        optMenu.add(frameOnTopOnSpeedChange);
172        frameOnTopOnSpeedChange.addActionListener(e -> {
173            for (Object ob : trainsPanel.getComponents()) {
174                if (ob instanceof AutoTrainControl) {
175                    AutoTrainControl atnn = (AutoTrainControl) ob;
176                    atnn.setOnTopOnSpeedChange(frameOnTopOnSpeedChange.isSelected());
177                }
178            }
179        });
180
181        menuBar.add(optMenu);
182
183        setJMenuBar(menuBar);
184        autoTrainsFrame.addHelpMenu("package.jmri.jmrit.dispatcher.AutoTrains", true);
185        trainsPanel.setLayout(new BoxLayout(trainsPanel, BoxLayout.Y_AXIS));
186        JPanel pB = new JPanel();
187        pB.setLayout(new FlowLayout());
188        JButton stopAllButton = new JButton(Bundle.getMessage("StopAll"));
189        pB.add(stopAllButton);
190        stopAllButton.addActionListener(this::stopAllPressed);
191        stopAllButton.setToolTipText(Bundle.getMessage("StopAllButtonHint"));
192        trainsPanel.add(pB);
193        trainsPanel.add(new JSeparator());
194        trainsPanel.addComponentListener(this);
195        trainsPanel.setVisible(true);
196        trainsPanel.revalidate();
197        trainScrollPanel = new JScrollPane();
198        trainScrollPanel.getViewport().add(trainsPanel);
199        autoTrainsFrame.getContentPane().setLayout(new BoxLayout(autoTrainsFrame.getContentPane(), BoxLayout.Y_AXIS));
200        autoTrainsFrame.getContentPane().add(trainScrollPanel);
201        setScrollBars();
202        autoTrainsFrame.getContentPane().revalidate();
203        autoTrainsFrame.pack();
204        autoTrainsFrame.setVisible(true);
205
206    }
207
208    private void setScrollBars() {
209        if (frameHasScrollBars.isSelected()) {
210            trainScrollPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
211            trainScrollPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
212            autoTrainsFrame.getContentPane().revalidate();
213        } else {
214            trainScrollPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
215            trainScrollPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
216            autoTrainsFrame.getContentPane().revalidate();
217        }
218    }
219
220    private void stopAllPressed(ActionEvent e) {
221        for (Object ob: trainsPanel.getComponents()) {
222            if (ob instanceof AutoTrainControl) {
223                ((AutoTrainControl) ob).stopAll();
224            }
225        }
226    }
227
228    @Override
229    public void dispose() {
230        if (prefMan!=null) {
231            prefMan.setSimplePreferenceState(hasScrollBars, frameHasScrollBars.isSelected());
232            prefMan.setSimplePreferenceState(canFloat, trainsCanBeFloated.isSelected());
233            prefMan.setSimplePreferenceState(hasScrollBars, frameHasScrollBars.isSelected());
234            prefMan.setSimplePreferenceState(canFloat, trainsCanBeFloated.isSelected());
235        }
236        super.dispose();
237    }
238    String hasScrollBars = this.getClass().getName() + ".HasScrollBars"; // NOI18N
239    String canFloat = this.getClass().getName() + ".CanFloat"; // NOI18N
240    String alWaysOnTop = this.getClass().getName() + ".AlWaysOnTop"; // NOI18N
241    String onTopOnSpeedChange = this.getClass().getName() + ".OnTopOnSpeedChange"; // NOI18N
242    class AutoTrainControl extends JPanel {
243
244        private boolean useOnTopOnSpeedChange;
245
246        public AutoTrainControl(AutoActiveTrain autoActiveTrain) {
247            this.autoActiveTrain = autoActiveTrain;
248            activeTrain = autoActiveTrain.getActiveTrain();
249            activeTrain.addPropertyChangeListener(activeTrainListener = new java.beans.PropertyChangeListener() {
250                @Override
251                public void propertyChange(java.beans.PropertyChangeEvent e) {
252                    handleActiveTrainListen(e);
253                }
254            });
255            rosterEntry = autoActiveTrain.getRosterEntry();
256            drawComponent();
257        }
258
259        protected void setOnTopOnSpeedChange(boolean value) {
260            useOnTopOnSpeedChange = value;
261        }
262
263        protected void stopAll() {
264            if (activeTrain.getStatus() != ActiveTrain.STOPPED &&
265                    activeTrain.getStatus() != ActiveTrain.DONE) {
266                autoActiveTrain.saveSpeedAndDirection();
267                autoActiveTrain.getAutoEngineer().setHalt(true);
268                autoActiveTrain.setSavedStatus(activeTrain.getStatus());
269                activeTrain.setStatus(ActiveTrain.STOPPED);
270            }
271        }
272
273        private AutoActiveTrain autoActiveTrain = null;
274        private java.beans.PropertyChangeListener activeTrainListener = null;
275        private java.beans.PropertyChangeListener throttleListener = null;
276        private jmri.Throttle throttle = null;
277        private ActiveTrain activeTrain = null;
278        private RosterEntry rosterEntry = null;
279
280        private JLabel trainLabel;
281        private JLabel throttleStatus;
282        protected JButton stopButton;
283        private JButton resumeAutoRunningButton;
284        private JRadioButton forwardButton;
285        private JRadioButton reverseButton;
286        private JSlider speedSlider;
287        private JButton manualButton;
288
289        private void handleThrottleListen(java.beans.PropertyChangeEvent e) {
290            if (!e.getPropertyName().equals(Throttle.SPEEDSETTING) && !e.getPropertyName().equals(Throttle.ISFORWARD)) {
291                return; // ignore if not speed or direction
292            }
293            updateThrottleDisplay(e);
294        }
295
296        private float lastReportedSpeed;   // for display purposes
297
298        /*
299         * Updates screen control throttle.
300         */
301        private void primeThrottleDisplay() {
302            if (throttle != null) {
303                if (throttle.getIsForward()) {
304                    forwardButton.setSelected(true);
305                } else {
306                    reverseButton.setSelected(true);
307                }
308                lastReportedSpeed = throttle.getSpeedSetting();
309                if (speedSlider.isVisible()) {
310                    speedSlider.setValue(Math.round(lastReportedSpeed * 100.0f));
311                }
312            }
313            updateThrottleStatus();
314        }
315
316        /*
317         * Updates control from events
318         */
319        private void updateThrottleDisplay(java.beans.PropertyChangeEvent e) {
320            if (throttle != null) {
321                if (e.getPropertyName().equals(Throttle.ISFORWARD)) {
322                    if ((boolean) e.getNewValue()) {
323                        forwardButton.setSelected(true);
324                    } else {
325                        reverseButton.setSelected(true);
326                    }
327                } else {
328                    lastReportedSpeed = (float) e.getNewValue();
329                    if (speedSlider.isVisible()) {
330                        speedSlider.setValue(Math.round(lastReportedSpeed * 100.0f));
331                    }
332                }
333            }
334            updateThrottleStatus();
335        }
336
337        /*
338         * Updates the status words.
339         */
340        private void updateThrottleStatus() {
341            StringBuilder sb = new StringBuilder();
342            if (throttle != null && throttleStatus.isVisible()) {
343                if (rosterEntry != null && autoActiveTrain.useSpeedProfile   && rosterEntry.getSpeedProfile() != null) {
344                    sb.append("" +
345                            rosterEntry.getSpeedProfile().convertThrottleSettingToScaleSpeedWithUnits(
346                                    lastReportedSpeed,
347                                    forwardButton.isSelected()));
348                } else {
349                    sb.append("" + Math.round(throttle.getSpeedSetting() * 100));
350                    sb.append("% ");
351                }
352                if (forwardButton.isSelected()) {
353                    sb.append("(fwd)");
354                } else {
355                    sb.append("(rev)");
356                }
357                throttleStatus.setText(sb.toString());
358                if (useOnTopOnSpeedChange) {
359                    // bring to front without getting focus
360                    setAlwaysOnTop(true);
361                    setAlwaysOnTop(false);
362                }
363            } else if (throttleStatus.isVisible()) {
364                throttleStatus.setText("No Throttle");
365            }
366        }
367
368        private void handleActiveTrainListen(java.beans.PropertyChangeEvent e) {
369            if (e.getNewValue() != null) {
370                log.trace("Property[{}] newValue[{}]",e.getPropertyName(),e.getNewValue());
371            } else {
372                log.trace("Property[{}] newValue[{}]",e.getPropertyName(),"NULL");
373            }
374            if (e.getPropertyName().equals("mode")) {
375                int newValue = ((Integer) e.getNewValue()).intValue();
376                int oldValue = ((Integer) e.getOldValue()).intValue();
377                if (newValue == ActiveTrain.DISPATCHED) {
378                    stopButton.setVisible(false);
379                    manualButton.setVisible(false);
380                    resumeAutoRunningButton.setVisible(true);
381                    forwardButton.setVisible(false);
382                    reverseButton.setVisible(false);
383                    speedSlider.setVisible(false);
384                    throttleStatus.setVisible(false);
385                    jmri.InstanceManager.throttleManagerInstance().removeListener(throttle.getLocoAddress(),
386                            throttleListener);
387                } else if (newValue == ActiveTrain.AUTOMATIC) {
388                    log.trace("[{}]:Set auto", autoActiveTrain.getActiveTrain().getActiveTrainName());
389                    if (throttle == null && autoActiveTrain.getThrottle() != null
390                            || oldValue == ActiveTrain.DISPATCHED) {
391                        log.trace("[{}]:Set new throttle", autoActiveTrain.getActiveTrain().getActiveTrainName());
392                        throttle = autoActiveTrain.getThrottle();
393                        throttleListener = new java.beans.PropertyChangeListener() {
394                            @Override
395                            public void propertyChange(java.beans.PropertyChangeEvent e) {
396                                handleThrottleListen(e);
397                            }
398                        };
399                        jmri.InstanceManager.throttleManagerInstance().attachListener(throttle.getLocoAddress(), throttleListener);
400                        rosterEntry = autoActiveTrain.getRosterEntry();
401                        setStatusLabelWidth();
402                        stopButton.setText(Bundle.getMessage("StopButton"));
403                        stopButton.setToolTipText(Bundle.getMessage("StopButtonHint"));
404                        stopButton.setVisible(true);
405                        manualButton.setText(Bundle.getMessage("ToManualButton"));
406                        manualButton.setToolTipText(Bundle.getMessage("ToManualButtonHint"));
407                        manualButton.setVisible(true);
408                        resumeAutoRunningButton.setVisible(false);
409                        forwardButton.setVisible(false);
410                        reverseButton.setVisible(false);
411                        speedSlider.setVisible(false);
412                        throttleStatus.setVisible(true);
413                        primeThrottleDisplay();
414                    }
415                } else if ((int) e.getNewValue() == ActiveTrain.TERMINATED) {
416                    if (throttle != null && throttleListener != null) {
417                        throttle.removePropertyChangeListener(throttleListener);
418                        throttle = null;
419                    }
420                    activeTrain.removePropertyChangeListener(activeTrainListener);
421                    // please someone stop me before I do something silly
422                    firePropertyChange("terminated", null, null);
423                }
424            } else if (e.getPropertyName().equals("status")) {
425                log.debug("NewStatus[{}]", e.getNewValue());
426                if ((int) e.getNewValue() == ActiveTrain.STOPPED) {
427                    stopButton.setText(Bundle.getMessage("ResumeButton"));
428                    stopButton.setToolTipText(Bundle.getMessage("ResumeButtonHint"));
429                    stopButton.setVisible(true);
430                } else if ((int) e.getNewValue() == ActiveTrain.RUNNING ||
431                        (int) e.getNewValue() == ActiveTrain.WAITING ||
432                        (int) e.getNewValue() == ActiveTrain.READY ) {
433                    log.trace("[{}]:Set auto STATUS RUNNING", autoActiveTrain.getActiveTrain().getActiveTrainName());
434                    if (throttle == null && autoActiveTrain.getThrottle() != null) {
435                        log.debug("[{}]:Set new throttle", autoActiveTrain.getActiveTrain().getActiveTrainName());
436                        throttle = autoActiveTrain.getThrottle();
437                        throttleListener = new java.beans.PropertyChangeListener() {
438                            @Override
439                            public void propertyChange(java.beans.PropertyChangeEvent e) {
440                                handleThrottleListen(e);
441                            }
442                        };
443                        jmri.InstanceManager.throttleManagerInstance().attachListener(throttle.getLocoAddress(), throttleListener);
444                        rosterEntry = autoActiveTrain.getRosterEntry();
445                        setStatusLabelWidth();
446                    }
447                    stopButton.setText(Bundle.getMessage("StopButton"));
448                    stopButton.setToolTipText(Bundle.getMessage("StopButtonHint"));
449                    stopButton.setVisible(true);
450                    manualButton.setText(Bundle.getMessage("ToManualButton"));
451                    manualButton.setToolTipText(Bundle.getMessage("ToManualButtonHint"));
452                    manualButton.setVisible(true);
453                    resumeAutoRunningButton.setVisible(false);
454                    forwardButton.setVisible(false);
455                    reverseButton.setVisible(false);
456                    speedSlider.setVisible(false);
457                    throttleStatus.setVisible(true);
458                    primeThrottleDisplay();
459                } else if ((int) e.getNewValue() == ActiveTrain.DONE) {
460                    stopButton.setText(Bundle.getMessage("RestartButton"));
461                    stopButton.setToolTipText(Bundle.getMessage("RestartButtonHint"));
462                    stopButton.setVisible(true);
463                } else {
464                    log.debug("Ignored newstatus[{}]", e.getNewValue());
465                }
466            }
467            pack();
468        }
469
470        public void manualAutoTrain() {
471            if (activeTrain.getMode() == ActiveTrain.AUTOMATIC) {
472                activeTrain.setMode(ActiveTrain.MANUAL);
473                manualButton.setText(Bundle.getMessage("ToAutoButton"));
474                manualButton.setToolTipText(Bundle.getMessage("ToAutoButtonHint"));
475                forwardButton.setVisible(true);
476                reverseButton.setVisible(true);
477                speedSlider.setVisible(true);
478                if (autoActiveTrain.getAutoEngineer() != null) {
479                    autoActiveTrain.saveSpeedAndDirection();
480                    autoActiveTrain.getAutoEngineer().setHalt(true);
481                    autoActiveTrain.setTargetSpeed(0.0f);
482                    autoActiveTrain.waitUntilStopped();
483                    autoActiveTrain.getAutoEngineer().setHalt(false);
484                    if (throttle.getIsForward() ) {
485                        forwardButton.setSelected(true);
486                    } else {
487                        reverseButton.setSelected(true);
488                    }
489                }
490
491            } else if (activeTrain.getMode() == ActiveTrain.MANUAL) {
492                activeTrain.setMode(ActiveTrain.AUTOMATIC);
493                manualButton.setText(Bundle.getMessage("ToManualButton"));
494                manualButton.setToolTipText(Bundle.getMessage("ToManualButtonHint"));
495                manualButton.setVisible(true);
496                forwardButton.setVisible(false);
497                reverseButton.setVisible(false);
498                speedSlider.setVisible(false);
499                autoActiveTrain.restoreSavedSpeedAndDirection();
500                // autoActiveTrain.setForward(!autoActiveTrain.getRunInReverse());
501                if ((activeTrain.getStatus() == ActiveTrain.RUNNING) ||
502                        (activeTrain.getStatus() == ActiveTrain.WAITING)) {
503                    autoActiveTrain.setSpeedBySignal();
504                }
505            }
506            pack();
507        }
508
509        private JToolBar componentJPanel;
510
511        private void drawComponent() {
512
513            componentJPanel = new JToolBar();
514            componentJPanel.setLayout(new FlowLayout());
515            componentJPanel.setFloatable(true);
516            trainLabel = new JLabel(autoActiveTrain.getActiveTrain().getTrainName());
517            trainLabel.setVisible(true);
518            componentJPanel.add(trainLabel);
519            stopButton = new JButton(Bundle.getMessage("ResumeButton"));
520            componentJPanel.add(stopButton);
521            stopButton.addActionListener(e -> stopResume());
522            manualButton = new JButton(Bundle.getMessage("ToManualButton"));
523            componentJPanel.add(manualButton);
524            manualButton.addActionListener(e -> manualAutoTrain());
525            resumeAutoRunningButton = new JButton(Bundle.getMessage("ResumeAutoButton"));
526            componentJPanel.add(resumeAutoRunningButton);
527            resumeAutoRunningButton.addActionListener(e -> resumeAutoOperation());
528            resumeAutoRunningButton.setVisible(false);
529            resumeAutoRunningButton.setToolTipText(Bundle.getMessage("ResumeAutoButtonHint"));
530            ButtonGroup directionGroup = new ButtonGroup();
531            forwardButton = new JRadioButton(Bundle.getMessage("ForwardRadio"));
532            componentJPanel.add(forwardButton);
533            forwardButton.addActionListener(e -> directionButton());
534            directionGroup.add(forwardButton);
535            reverseButton = new JRadioButton(Bundle.getMessage("ReverseRadio"));
536            componentJPanel.add(reverseButton);
537            reverseButton.addActionListener(e -> directionButton());
538            directionGroup.add(reverseButton);
539            speedSlider = new JSlider(0, 100, 0);
540            speedSlider.setPreferredSize(new Dimension(100, 20));
541            componentJPanel.add(speedSlider);
542            speedSlider.addChangeListener(e -> {
543                if (speedSlider.isVisible()) {
544                    int val = ((JSlider) (e.getSource())).getValue();
545                    float speedValue = val * 0.01f;
546                    // bypass auto-engineer limits, ramping etc
547                    // when in manual.
548                    autoActiveTrain.getThrottle().setSpeedSetting(speedValue);
549                }
550            });
551
552            throttleStatus = new JLabel();
553            // prevent JFrame to resize on each % change - temporary size for initialization
554            throttleStatus.setPreferredSize(new Dimension(100, 20));
555            throttleStatus.setText("Speed Unknown");
556            componentJPanel.add(throttleStatus);
557            componentJPanel.revalidate();
558            add(componentJPanel, BorderLayout.EAST);
559            pack();
560        }
561
562        /*
563         * Using dummy strings get max size of the statustext
564         */
565        private void setStatusLabelWidth() {
566            if (rosterEntry!=null && autoActiveTrain.getUseSpeedProfile()) {
567                throttleStatus.setPreferredSize(
568                        new JTextField(20).getPreferredSize());
569            } else {
570                throttleStatus.setPreferredSize(
571                        new JTextField(10).getPreferredSize());
572            }
573        }
574        public void stopResume() {
575            if (autoActiveTrain.getAutoEngineer() != null) {
576                ActiveTrain at = autoActiveTrain.getActiveTrain();
577                if (at.getStatus() == ActiveTrain.STOPPED) {
578                    log.trace("Train Is Stopped - Resume");
579                    if (autoActiveTrain.getCurrentBlock().getState() != Block.OCCUPIED) {
580                        JmriJOptionPane.showMessageDialog(
581                                this,
582                                Bundle.getMessage("AutoTrainsFramePleaseMoveTrain",autoActiveTrain.getCurrentBlock().getDisplayName()),
583                                Bundle.getMessage("ResumeAutoButton"),
584                                JmriJOptionPane.INFORMATION_MESSAGE
585                            );
586                        return;
587                    }
588                    Block b = autoActiveTrain.isBlockAhead();
589                    if (b != null) {
590                        JmriJOptionPane.showMessageDialog(
591                                this,
592                                Bundle.getMessage("AutoTrainsFrameUnExplainedOccupancy",
593                                        b.getDisplayName(),
594                                        autoActiveTrain.getNextBlock()),
595                                Bundle.getMessage("ResumeAutoButton"),
596                                JmriJOptionPane.INFORMATION_MESSAGE
597                            );
598                        return;
599                    }
600
601                    boolean tmpIsForward = true;
602                    if ( autoActiveTrain.getRunInReverse() !=  at.isTransitReversed()) {
603                        tmpIsForward = false;
604                    }
605                    Object[] options = {Bundle.getMessage("AutoTrainsFrameUseImpliedDirection",getDirString(tmpIsForward)),
606                            Bundle.getMessage("AutoTrainsFrameRestoreSaved",getDirString(autoActiveTrain.getSavedDirection())),
607                            Bundle.getMessage("AutoTrainsFrameUseCurrent",getDirString(autoActiveTrain.getForward())) };
608                    if ( tmpIsForward != autoActiveTrain.getSavedDirection() ||
609                            tmpIsForward != autoActiveTrain.getForward()) {
610                        int retval = JmriJOptionPane.showOptionDialog(this,
611                                Bundle.getMessage("AutoTrainsFrameWhichDirectionToUse"),
612                                Bundle.getMessage("AutoTrainsFrameDirectionConflict"),
613                                JmriJOptionPane.YES_NO_OPTION,
614                                JmriJOptionPane.QUESTION_MESSAGE, null, options, options[1]);
615                        switch (retval) {
616                            case 0:
617                                autoActiveTrain.setEngineDirection(tmpIsForward);
618                                break;
619                            case 1:
620                                autoActiveTrain.setEngineDirection(autoActiveTrain.getSavedDirection());
621                                break;
622                            case 2:
623                                autoActiveTrain.setEngineDirection(autoActiveTrain.getForward());
624                                break;
625                            default:
626                                // do nothing.
627                                log.info("rturneded[{}]",retval);
628                        }
629                    }
630                    autoActiveTrain.getAutoEngineer().setHalt(false);
631                    at.setStatus(autoActiveTrain.getSavedStatus());
632                    if (at.getStatus() == ActiveTrain.STOPPED) {
633                        at.setStatus(ActiveTrain.WAITING);
634                    }
635                    if ((at.getStatus() == ActiveTrain.RUNNING) || (at.getStatus() == ActiveTrain.WAITING)) {
636                        autoActiveTrain.setSpeedBySignal();
637                    }
638                } else if (at.getStatus() == ActiveTrain.READY) {
639                    handleActiveTrainListen(new java.beans.PropertyChangeEvent (this,"status", Integer.valueOf(0), Integer.valueOf(ActiveTrain.READY)));
640                } else if (at.getStatus() == ActiveTrain.DONE) {
641                    log.trace("Train Is Done - Restart");
642                    // restart
643                    at.allocateAFresh();
644                    at.restart();
645                } else {
646                    log.trace("Process As Stop");
647                    // stop
648                    autoActiveTrain.saveSpeedAndDirection();
649                    autoActiveTrain.getAutoEngineer().setHalt(true);
650                    autoActiveTrain.setSavedStatus(at.getStatus());
651                    at.setStatus(ActiveTrain.STOPPED);
652                    speedSlider.setValue(0);
653                }
654            } else {
655                log.error("unexpected null autoEngineer");
656            }
657        }
658
659        private String getDirString(boolean isFwd) {
660            return isFwd ? Bundle.getMessage("Fwd") : Bundle.getMessage("Rev");
661        }
662
663        public void resumeAutoOperation() {
664            autoActiveTrain.resumeAutomaticRunning();
665        }
666
667        public void directionButton() {
668            ActiveTrain at = autoActiveTrain.getActiveTrain();
669            if (at.getMode() == ActiveTrain.MANUAL) {
670                autoActiveTrain.setForward(forwardButton.isSelected());
671            } else {
672                log.debug(" {}:Ignored direction button change, not in manual mode", at.getTrainName());
673            }
674        }
675    }
676
677    private static final Logger log = LoggerFactory.getLogger(AutoTrainsFrame.class);
678
679}
680
681