001package jmri.jmrit.beantable;
002
003import java.awt.BorderLayout;
004import java.awt.Component;
005import java.awt.Container;
006import java.awt.Dimension;
007import java.awt.FlowLayout;
008import java.awt.event.ActionEvent;
009import java.util.ArrayList;
010import java.util.Arrays;
011import java.util.List;
012import java.util.ResourceBundle;
013import java.util.Set;
014
015import javax.annotation.Nonnull;
016import javax.swing.*;
017import javax.swing.border.TitledBorder;
018import javax.swing.event.ChangeEvent;
019import javax.swing.table.DefaultTableCellRenderer;
020import javax.swing.table.TableCellEditor;
021import javax.swing.table.TableColumn;
022import javax.swing.table.TableColumnModel;
023
024import jmri.*;
025import jmri.NamedBean.DisplayOptions;
026import jmri.jmrit.dispatcher.TrainInfoFile;
027import jmri.jmrit.roster.RosterEntry;
028import jmri.jmrit.roster.swing.RosterEntryComboBox;
029import jmri.util.JmriJFrame;
030import jmri.swing.NamedBeanComboBox;
031import jmri.util.swing.JComboBoxUtil;
032import jmri.util.swing.JmriJOptionPane;
033import jmri.util.table.ButtonEditor;
034import jmri.util.table.ButtonRenderer;
035
036/**
037 * Swing action to create and register a TransitTable GUI.
038 *
039 * @author Dave Duchamp Copyright (C) 2008, 2010, 2011
040 */
041public class TransitTableAction extends AbstractTableAction<Transit> {
042
043    /**
044     * Create an action with a specific title.
045     * <p>
046     * Note that the argument is the Action title, not the title of the
047     * resulting frame. Perhaps this should be changed?
048     *
049     * @param actionName action title
050     */
051    public TransitTableAction(String actionName) {
052        super(actionName);
053
054        transitManager = InstanceManager.getNullableDefault(TransitManager.class);
055        // disable ourself if there is no Transit manager available
056        if (sectionManager == null || transitManager == null) {
057            super.setEnabled(false);
058        }
059        updateSensorList();
060    }
061
062    public TransitTableAction() {
063        this(Bundle.getMessage("TitleTransitTable"));
064    }
065
066    static final ResourceBundle rbx = ResourceBundle.getBundle("jmri.jmrit.beantable.SectionTransitTableBundle");
067
068    /**
069     * Create the JTable DataModel, along with the changes for the specific case
070     * of Transit objects.
071     */
072    @Override
073    protected void createModel() {
074        m = new BeanTableDataModel<Transit>() {
075
076            static public final int EDITCOL = NUMCOLUMN;
077            static public final int DUPLICATECOL = EDITCOL + 1;
078
079            @Override
080            public String getValue(String name) {
081                if (name == null) {
082                    log.warn("requested getValue(null)");
083                    return "(no name)";
084                }
085                Transit z = InstanceManager.getDefault(TransitManager.class).getBySystemName(name);
086                if (z == null) {
087                    log.debug("requested getValue(\"{}\"), Transit doesn't exist", name);
088                    return "(no Transit)";
089                }
090                return "Transit";
091            }
092
093            @Override
094            public TransitManager getManager() {
095                return InstanceManager.getDefault(TransitManager.class);
096            }
097
098            @Override
099            public Transit getBySystemName(@Nonnull String name) {
100                return InstanceManager.getDefault(TransitManager.class).getBySystemName(name);
101            }
102
103            @Override
104            public Transit getByUserName(@Nonnull String name) {
105                return InstanceManager.getDefault(TransitManager.class).getByUserName(name);
106            }
107
108            @Override
109            protected String getMasterClassName() {
110                return getClassName();
111            }
112
113            @Override
114            public void clickOn(Transit t) {
115            }
116
117            @Override
118            public int getColumnCount() {
119                return DUPLICATECOL + 1;
120            }
121
122            @Override
123            public Object getValueAt(int row, int col) {
124                switch (col) {
125                    case VALUECOL:
126                        // some error checking
127                        if (row >= sysNameList.size()) {
128                            log.debug("row is greater than name list");
129                            return "";
130                        }   Transit z = getBySystemName(sysNameList.get(row));
131                        if (z == null) {
132                            return "";
133                        } else {
134                            int state = z.getState();
135                            if (state == Transit.IDLE) {
136                                return (rbx.getString("TransitIdle"));
137                            } else if (state == Transit.ASSIGNED) {
138                                return (rbx.getString("TransitAssigned"));
139                            }
140                        }   break;
141                    case EDITCOL:
142                        return Bundle.getMessage("ButtonEdit");
143                    case DUPLICATECOL:
144                        return rbx.getString("ButtonDuplicate");
145                    default:
146                        return super.getValueAt(row, col);
147                }
148                return null;
149            }
150
151            @Override
152            public void setValueAt(Object value, int row, int col) {
153                switch (col) {
154                    case EDITCOL:
155                        SwingUtilities.invokeLater(() -> {
156                            editPressed(((Transit) getValueAt(row, SYSNAMECOL)).getSystemName());
157                        });
158                        break;
159                    case DUPLICATECOL:
160                        SwingUtilities.invokeLater(() -> {
161                            duplicatePressed(((Transit) getValueAt(row, SYSNAMECOL)).getSystemName());
162                        });
163                        break;
164                    default:
165                        super.setValueAt(value, row, col);
166                        break;
167                }
168            }
169
170            @Override
171            public String getColumnName(int col) {
172                switch (col) {
173                    case EDITCOL: // no name on Edit column
174                    case DUPLICATECOL: // no name on Duplicate column
175                        return "";
176                    default:
177                        return super.getColumnName(col);
178                }
179            }
180
181            @Override
182            public Class<?> getColumnClass(int col) {
183                switch (col) {
184                    case VALUECOL:
185                        return String.class; // not a button
186                    case EDITCOL:
187                    case DUPLICATECOL:
188                        return JButton.class;
189                    default:
190                        return super.getColumnClass(col);
191                }
192            }
193
194            @Override
195            public boolean isCellEditable(int row, int col) {
196                switch (col) {
197                    case VALUECOL:
198                        return false;
199                    case EDITCOL:
200                    case DUPLICATECOL:
201                        return true;
202                    default:
203                        return super.isCellEditable(row, col);
204                }
205            }
206
207            @Override
208            public int getPreferredWidth(int col) {
209                // override default value for SystemName and UserName columns
210                switch (col) {
211                    case SYSNAMECOL:
212                        return new JTextField(9).getPreferredSize().width;
213                    case USERNAMECOL:
214                        return new JTextField(17).getPreferredSize().width;
215                    case VALUECOL:
216                    case EDITCOL:
217                        return new JTextField(6).getPreferredSize().width;
218                    case DUPLICATECOL:
219                        return new JTextField(10).getPreferredSize().width;
220                    default:
221                        return super.getPreferredWidth(col);
222                }
223            }
224
225            @Override
226            public void configValueColumn(JTable table) {
227                // value column isn't a button, so config is null
228            }
229
230            @Override
231            protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
232                return true;
233                // return (e.getPropertyName().indexOf("alue")=0);
234            }
235
236            @Override
237            public JButton configureButton() {
238                log.error("configureButton should not have been called");
239                return null;
240            }
241
242            @Override
243            public void configureTable(JTable table) {
244                InstanceManager.getDefault(SensorManager.class).addPropertyChangeListener(this);
245                super.configureTable(table);
246            }
247
248            @Override
249            public void propertyChange(java.beans.PropertyChangeEvent e) {
250                if (e.getSource() instanceof SensorManager) {
251                    if (e.getPropertyName().equals("DisplayListName") || e.getPropertyName().equals("length")) {
252                        updateSensorList();
253                    }
254                }
255                super.propertyChange(e);
256            }
257
258            @Override
259            public void dispose(){
260                InstanceManager.getDefault(SensorManager.class).removePropertyChangeListener(this);
261                super.dispose();
262            }
263
264        };
265    }
266
267    @Override
268    protected void setTitle() {
269        f.setTitle(Bundle.getMessage("TitleTransitTable"));
270    }
271
272    @Override
273    protected String helpTarget() {
274        return "package.jmri.jmrit.beantable.TransitTable";
275    }
276
277    // instance variables
278    private boolean editMode = false;
279    private boolean duplicateMode = false;
280    private TransitManager transitManager = null;
281    private final SectionManager sectionManager = InstanceManager.getNullableDefault(SectionManager.class);
282    private Transit curTransit = null;
283    private SectionTableModel sectionTableModel = null;
284    private final List<Section> sectionList = new ArrayList<>();
285    private final List<Integer> direction = new ArrayList<>();
286    private final List<Integer> sequence = new ArrayList<>();
287    private final List<List<TransitSectionAction>> action = new ArrayList<>();
288    private final List<Boolean> alternate = new ArrayList<>();
289    private final List<Boolean> safe = new ArrayList<>();
290    private String[] sensorList;
291    private final List<String> sensorStopAllocation = new ArrayList<>();
292    private final List<Float> fwdStopPerCent = new ArrayList<>();
293    private final List<Float> revStopPerCent = new ArrayList<>();
294    private final List<Section> primarySectionBoxList = new ArrayList<>();
295    private final List<Integer> priSectionDirection = new ArrayList<>();
296    private final List<Section> alternateSectionBoxList = new ArrayList<>();
297    private final List<Integer> altSectionDirection = new ArrayList<>();
298    private final List<Section> insertAtBeginningBoxList = new ArrayList<>();
299    private final List<Integer> insertAtBeginningDirection = new ArrayList<>();
300    private Section curSection = null;
301    private int curSectionDirection = 0;
302    private Section prevSection = null;
303    private int prevSectionDirection = 0;
304    private int curSequenceNum = 0;
305
306    // add/create variables
307    JmriJFrame addFrame = null;
308    JTextField sysName = new JTextField(15);
309    JLabel sysNameFixed = new JLabel("");
310    JTextField userName = new JTextField(17);
311    JLabel sysNameLabel = new JLabel(Bundle.getMessage("LabelSystemName"));
312    JLabel userNameLabel = new JLabel(Bundle.getMessage("LabelUserName"));
313    JButton create = null;
314    JButton update = null;
315    JButton deleteSections = null;
316    JComboBox<String> primarySectionBox = new JComboBox<>();
317    JButton addNextSection = null;
318    JCheckBox addAsSafe = null;
319    JComboBox<String> stopAllocatingSensorBox = new JComboBox<>();
320    JButton removeLastSection = null;
321    JButton removeFirstSection = null;
322    JButton insertAtBeginning = null;
323    JComboBox<String> insertAtBeginningBox = new JComboBox<>();
324    JLabel seqNumLabel = new JLabel(rbx.getString("LabelSeqNum"));
325    JSpinner seqNum = new JSpinner(new SpinnerNumberModel(1, 1, 1, 1));
326    JButton replacePrimaryForSequence = null;
327    JButton deleteAlternateForSequence = null;
328    JButton addAlternateForSequence = null;
329    JComboBox<String> alternateSectionBox = new JComboBox<>();
330    JButton addAlternateSection = null;
331    JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName"));
332    UserPreferencesManager pref;
333    String systemNameAuto = this.getClass().getName() + ".AutoSystemName";
334
335
336    /**
337     * Responds to the Add...button and the Edit buttons in Transit Table.
338     * @param e Event causing  method call.
339     */
340    @Override
341    protected void addPressed(ActionEvent e) {
342        editMode = false;
343        duplicateMode = false;
344        if ((sectionManager.getNamedBeanSet().size()) > 0) {
345            addEditPressed();
346        } else {
347            JmriJOptionPane.showMessageDialog(null, rbx
348                    .getString("Message21"), Bundle.getMessage("ErrorTitle"),
349                    JmriJOptionPane.ERROR_MESSAGE);
350        }
351    }
352
353    void editPressed(String sName) {
354        curTransit = transitManager.getBySystemName(sName);
355        if (curTransit == null) {
356            // no transit - should never happen, but protects against a $%^#@ exception
357            return;
358        }
359        sysNameFixed.setText(sName);
360        editMode = true;
361        duplicateMode = false;
362        addEditPressed();
363    }
364
365    void duplicatePressed(String sName) {
366        curTransit = transitManager.getBySystemName(sName);
367        if (curTransit == null) {
368            // no transit - should never happen, but protects against a $%^#@ exception
369            return;
370        }
371        duplicateMode = true;
372        editMode = false;
373        addEditPressed();
374    }
375
376    void addEditPressed() {
377        pref = InstanceManager.getDefault(UserPreferencesManager.class);
378        if (addFrame == null) {
379            addFrame = new JmriJFrame(Bundle.getMessage("TitleAddTransit"));
380            addFrame.addHelpMenu("package.jmri.jmrit.beantable.TransitAddEdit", true);
381            addFrame.getContentPane().setLayout(new BoxLayout(addFrame.getContentPane(), BoxLayout.Y_AXIS));
382            JPanel p;
383            // system name
384            p = new JPanel();
385            p.setLayout(new FlowLayout());
386            p.add(sysNameLabel);
387            sysNameLabel.setLabelFor(sysName);
388            p.add(sysNameFixed);
389            p.add(sysName);
390            p.add(_autoSystemName);
391            _autoSystemName.addActionListener( e -> autoSystemName());
392            if (pref.getSimplePreferenceState(systemNameAuto)) {
393                _autoSystemName.setSelected(true);
394            }
395            sysName.setToolTipText(rbx.getString("TransitSystemNameHint"));
396            addFrame.getContentPane().add(p);
397            // user name
398            p = new JPanel();
399            p.add(userNameLabel);
400            userNameLabel.setLabelFor(userName);
401            p.add(userName);
402            userName.setToolTipText(rbx.getString("TransitUserNameHint"));
403            addFrame.getContentPane().add(p);
404            addFrame.getContentPane().add(new JSeparator());
405            // instruction text fields
406            JPanel p1 = new JPanel();
407            p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS));
408            JPanel p11 = new JPanel();
409            p11.setLayout(new FlowLayout());
410            p11.add(new JLabel(rbx.getString("SectionTableMessage")));
411            p1.add(p11);
412            JPanel p12 = new JPanel();
413            p12.setLayout(new BorderLayout());
414            // initialize table of sections
415            sectionTableModel = new SectionTableModel();
416            JTable sectionTable = new JTable(sectionTableModel);
417            sectionTable.getTableHeader().setPreferredSize(new Dimension(0, new JLabel("<html>I<br>I</html>").getPreferredSize().height));
418            sectionTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
419            sectionTable.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
420            sectionTable.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
421            sectionTable.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer());
422
423            sectionTable.setRowSelectionAllowed(false);
424            TableColumnModel sectionColumnModel = sectionTable.getColumnModel();
425            TableColumn sequenceColumn = sectionColumnModel.getColumn(SectionTableModel.SEQUENCE_COLUMN);
426            sequenceColumn.setResizable(true);
427            sequenceColumn.setMinWidth(50);
428            sequenceColumn.setMaxWidth(70);
429            TableColumn sectionColumn = sectionColumnModel.getColumn(SectionTableModel.SECTIONNAME_COLUMN);
430            sectionColumn.setResizable(true);
431            sectionColumn.setMinWidth(150);
432            TableColumn actionColumn = sectionColumnModel.getColumn(SectionTableModel.ACTION_COLUMN);
433            // install button renderer and editor
434            ButtonRenderer buttonRenderer = new ButtonRenderer();
435            sectionTable.setDefaultRenderer(JButton.class, buttonRenderer);
436            TableCellEditor buttonEditor = new ButtonEditor(new JButton());
437            sectionTable.setDefaultEditor(JButton.class, buttonEditor);
438            JButton testButton = new JButton(rbx.getString("AddEditActions"));
439            sectionTable.setRowHeight(testButton.getPreferredSize().height);
440            actionColumn.setResizable(false);
441            actionColumn.setMinWidth(testButton.getPreferredSize().width);
442            TableColumn directionColumn = sectionColumnModel.getColumn(SectionTableModel.SEC_DIRECTION_COLUMN);
443            directionColumn.setResizable(true);
444            String s = rbx.getString("DirectionColName");
445            directionColumn.setMinWidth((int)new JLabel(s.substring(1, Math.min(s.length(), 7))).getPreferredSize().getWidth());
446            directionColumn.setMaxWidth((int)new JLabel(rbx.getString("DirectionColName").concat("WW")).getPreferredSize().getWidth());
447            TableColumn alternateColumn = sectionColumnModel.getColumn(SectionTableModel.ALTERNATE_COLUMN);
448            alternateColumn.setResizable(true);
449            s = rbx.getString("AlternateColName");
450            alternateColumn.setMinWidth((int)new JLabel(s.substring(1, Math.min(s.length(), 7))).getPreferredSize().getWidth());
451            alternateColumn.setMaxWidth((int)new JLabel(rbx.getString("AlternateColName").concat("WW")).getPreferredSize().getWidth());
452            sectionColumnModel.getColumn(SectionTableModel.FWD_STOPPING_LENGTH).setCellEditor(new JSpinnerCellEditor());
453            sectionColumnModel.getColumn(SectionTableModel.FWD_STOPPING_LENGTH).setCellRenderer(new JSpinnerCellRenderer());
454            sectionColumnModel.getColumn(SectionTableModel.REV_STOPPING_LENGTH).setCellEditor(new JSpinnerCellEditor());
455            sectionColumnModel.getColumn(SectionTableModel.REV_STOPPING_LENGTH).setCellRenderer(new JSpinnerCellRenderer());
456            JScrollPane sectionTableScrollPane = new JScrollPane(sectionTable);
457            p12.add(sectionTableScrollPane, BorderLayout.CENTER);
458            p1.add(p12);
459            JPanel p13 = new JPanel();
460            p13.add(primarySectionBox);
461            primarySectionBox.setToolTipText(rbx.getString("PrimarySectionBoxHint"));
462            p13.add(addNextSection = new JButton(rbx.getString("AddPrimaryButton")));
463            p13.add(addAsSafe = new JCheckBox(Bundle.getMessage("TransitSectionIsSafe")));
464            addAsSafe.setToolTipText(Bundle.getMessage("TransitSectionIsSafeHint"));
465            JPanel p13A = new JPanel();
466            p13A.add(new JLabel(Bundle.getMessage("PauseAllocationOnSensorActive")));
467            p13A.add(stopAllocatingSensorBox = new JComboBox<>(sensorList));
468            JComboBoxUtil.setupComboBoxMaxRows(stopAllocatingSensorBox);
469            p13.add(p13A);
470            stopAllocatingSensorBox.setToolTipText(Bundle.getMessage("PauseAllocationOnSensorActiveHint"));
471            addNextSection.addActionListener(this::addNextSectionPressed);
472            addNextSection.setToolTipText(rbx.getString("AddPrimaryButtonHint"));
473            p13.setLayout(new FlowLayout());
474            p1.add(p13);
475            JPanel p14 = new JPanel();
476            p14.setLayout(new FlowLayout());
477            p14.add(alternateSectionBox);
478            alternateSectionBox.setToolTipText(rbx.getString("AlternateSectionBoxHint"));
479            p14.add(addAlternateSection = new JButton(rbx.getString("AddAlternateButton")));
480            addAlternateSection.addActionListener(this::addAlternateSectionPressed);
481            addAlternateSection.setToolTipText(rbx.getString("AddAlternateButtonHint"));
482            p14.add(new JLabel("        ")); // spacer between 2 groups of label + combobox
483            p14.add(insertAtBeginningBox);
484            insertAtBeginningBox.setToolTipText(rbx.getString("InsertAtBeginningBoxHint"));
485            p14.add(insertAtBeginning = new JButton(rbx.getString("InsertAtBeginningButton")));
486            insertAtBeginning.addActionListener(this::insertAtBeginningPressed);
487            insertAtBeginning.setToolTipText(rbx.getString("InsertAtBeginningButtonHint"));
488            p1.add(p14);
489            p1.add(new JSeparator());
490            JPanel p15 = new JPanel();
491            p15.setLayout(new FlowLayout());
492            p15.add(deleteSections = new JButton(rbx.getString("DeleteSectionsButton")));
493            deleteSections.addActionListener(this::deleteAllSections);
494            deleteSections.setToolTipText(rbx.getString("DeleteSectionsButtonHint"));
495            p15.add(new JLabel("  "));
496            p15.add(removeLastSection = new JButton(rbx.getString("RemoveLastButton")));
497            removeLastSection.addActionListener(this::removeLastSectionPressed);
498            removeLastSection.setToolTipText(rbx.getString("RemoveLastButtonHint"));
499            p15.add(new JLabel("  "));
500            p15.add(removeFirstSection = new JButton(rbx.getString("RemoveFirstButton")));
501            removeFirstSection.addActionListener(this::removeFirstSectionPressed);
502            removeFirstSection.setToolTipText(rbx.getString("RemoveFirstButtonHint"));
503            p1.add(p15);
504            JPanel p16 = new JPanel();
505            p16.setLayout(new FlowLayout());
506            p16.add(seqNumLabel);
507            p16.add(seqNum);
508            seqNum.setToolTipText(rbx.getString("SeqNumHint"));
509            p1.add(p16);
510            JPanel p17 = new JPanel();
511            p17.setLayout(new FlowLayout());
512            p17.add(replacePrimaryForSequence = new JButton(rbx.getString("ReplacePrimaryForSeqButton")));
513            replacePrimaryForSequence.addActionListener(this::replacePrimaryForSeqPressed);
514            replacePrimaryForSequence.setToolTipText(rbx.getString("ReplacePrimaryForSeqButtonHint"));
515            p17.add(new JLabel("  "));
516            p17.add(deleteAlternateForSequence = new JButton(rbx.getString("DeleteAlternateForSeqButton")));
517            deleteAlternateForSequence.addActionListener(this::deleteAlternateForSeqPressed);
518            deleteAlternateForSequence.setToolTipText(rbx.getString("DeleteAlternateForSeqButtonHint"));
519            p17.add(new JLabel("  "));
520            p17.add(addAlternateForSequence = new JButton(rbx.getString("AddAlternateForSeqButton")));
521            addAlternateForSequence.addActionListener(this::addAlternateForSeqPressed);
522            addAlternateForSequence.setToolTipText(rbx.getString("AddAlternateForSeqButtonHint"));
523            p1.add(p17);
524            addFrame.getContentPane().add(p1);
525            // set up bottom buttons
526            addFrame.getContentPane().add(new JSeparator());
527            JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N
528            JPanel pb = new JPanel();
529            pb.setLayout(new FlowLayout());
530            pb.add(cancel );
531            cancel.addActionListener(this::cancelPressed);
532            cancel.setToolTipText(rbx.getString("CancelButtonHint"));
533            pb.add(create = new JButton(Bundle.getMessage("ButtonCreate")));
534            create.addActionListener(this::createPressed);
535            create.setToolTipText(rbx.getString("SectionCreateButtonHint"));
536            pb.add(update = new JButton(Bundle.getMessage("ButtonUpdate")));
537            update.addActionListener(this::updatePressed);
538            update.setToolTipText(rbx.getString("SectionUpdateButtonHint"));
539            addFrame.getContentPane().add(pb);
540        }
541        if (editMode) {
542            // setup for edit window
543            addFrame.setTitle(Bundle.getMessage("TitleEditTransit"));
544            _autoSystemName.setVisible(false);
545            sysNameLabel.setEnabled(true);
546            create.setVisible(false);
547            update.setVisible(true);
548            sysName.setVisible(false);
549            sysNameFixed.setVisible(true);
550            addFrame.getRootPane().setDefaultButton(update);
551            initializeEditInformation();
552        } else {
553            // setup for create window
554            addFrame.setTitle(Bundle.getMessage("TitleAddTransit"));
555            _autoSystemName.setVisible(true);
556            _autoSystemName.setEnabled(true);
557            autoSystemName();
558            create.setVisible(true);
559            create.setEnabled(true);
560            update.setVisible(false);
561            sysName.setVisible(true);
562            sysNameFixed.setVisible(false);
563            addFrame.getRootPane().setDefaultButton(create);
564            if (duplicateMode) {
565                // setup with information from previous Transit
566                initializeEditInformation();
567                sysName.setText(curTransit.getSystemName());
568                curTransit = null;
569            } else {
570                deleteAllSections(null);
571            }
572        }
573        initializeSectionCombos();
574        updateSeqNum();
575        addFrame.setEscapeKeyClosesWindow(true);
576        addFrame.pack();
577        addFrame.setVisible(true);
578    }
579
580    private void initializeEditInformation() {
581        sectionList.clear();
582        sequence.clear();
583        action.clear();
584        direction.clear();
585        alternate.clear();
586        safe.clear();
587        sensorStopAllocation.clear();
588        fwdStopPerCent.clear();
589        revStopPerCent.clear();
590
591        curSection = null;
592        curSectionDirection = 0;
593        curSequenceNum = 0;
594        prevSection = null;
595        prevSectionDirection = 0;
596        if (curTransit != null) {
597            userName.setText(curTransit.getUserName());
598            List<TransitSection> tsList = curTransit.getTransitSectionList();
599            for (int i = 0; i < tsList.size(); i++) {
600                TransitSection ts = tsList.get(i);
601                if (ts != null) {
602                    sectionList.add(ts.getSection());
603                    sequence.add(ts.getSequenceNumber());
604                    direction.add(ts.getDirection());
605                    action.add(ts.getTransitSectionActionList());
606                    alternate.add(ts.isAlternate());
607                    safe.add(ts.isSafe());
608                    sensorStopAllocation.add(ts.getStopAllocatingSensor());
609                    fwdStopPerCent.add(ts.getFwdStopPerCent());
610                    revStopPerCent.add(ts.getRevStopPerCent());
611                }
612            }
613            int index = sectionList.size() - 1;
614            if (index >= alternate.size()) {
615                index = alternate.size() - 1;
616            }
617            while (alternate.get(index) && (index > 0)) {
618                index--;
619            }
620            if (index >= 0) {
621                curSection = sectionList.get(index);
622                curSequenceNum = sequence.get(index);
623                if (index > 0) {
624                    curSectionDirection = direction.get(index);
625                }
626                index--;
627                while ((index >= 0) && alternate.get(index)) {
628                    index--;
629                }
630                if (index >= 0) {
631                    prevSection = sectionList.get(index);
632                    prevSectionDirection = direction.get(index);
633                }
634            }
635        }
636        sectionTableModel.fireTableDataChanged();
637    }
638
639    private void deleteAllSections(ActionEvent e) {
640        sectionList.clear();
641        direction.clear();
642        sequence.clear();
643        action.clear();
644        alternate.clear();
645        safe.clear();
646        sensorStopAllocation.clear();
647        fwdStopPerCent.clear();
648        revStopPerCent.clear();
649        curSection = null;
650        curSectionDirection = 0;
651        prevSection = null;
652        prevSectionDirection = 0;
653        curSequenceNum = 0;
654        initializeSectionCombos();
655        updateSeqNum();
656        sectionTableModel.fireTableDataChanged();
657    }
658
659    void addNextSectionPressed(ActionEvent e) {
660        if (primarySectionBoxList.isEmpty()) {
661            JmriJOptionPane.showMessageDialog(addFrame, rbx
662                    .getString("Message25"), Bundle.getMessage("ErrorTitle"),
663                    JmriJOptionPane.ERROR_MESSAGE);
664            return;
665        }
666        int index = primarySectionBox.getSelectedIndex();
667        Section s = primarySectionBoxList.get(index);
668        if (s != null) {
669            int j = sectionList.size();
670            sectionList.add(s);
671            direction.add(priSectionDirection.get(index));
672            curSequenceNum++;
673            sequence.add(curSequenceNum);
674            safe.add(addAsSafe.isSelected());
675            if (stopAllocatingSensorBox.getSelectedIndex() >= 0) {
676                sensorStopAllocation.add((String)stopAllocatingSensorBox.getSelectedItem());
677            } else {
678                sensorStopAllocation.add("");
679            }
680            action.add(new ArrayList<>());
681            alternate.add(false);
682            if ((sectionList.size() == 2) && (curSection != null)) {
683                if (forwardConnected(curSection, s, 0)) {
684                    direction.set(0, Section.REVERSE);
685                }
686                curSectionDirection = direction.get(0);
687            }
688            prevSection = curSection;
689            prevSectionDirection = curSectionDirection;
690            curSection = s;
691            if (prevSection != null) {
692                curSectionDirection = direction.get(j);
693            }
694            fwdStopPerCent.add(0, Float.valueOf(1.0f));
695            revStopPerCent.add(0, Float.valueOf(1.0f));
696            initializeSectionCombos();
697        }
698        updateSeqNum();
699        sectionTableModel.fireTableDataChanged();
700    }
701
702    void removeLastSectionPressed(ActionEvent e) {
703        if (sectionList.size() <= 1) {
704            deleteAllSections(e);
705        } else {
706            int j = sectionList.size() - 1;
707            if (!alternate.get(j)) {
708                curSequenceNum--;
709                curSection = sectionList.get(j - 1);
710                curSectionDirection = direction.get(j - 1);
711                // delete alternate if present
712                int k = j - 2;
713                while ((k >= 0) && alternate.get(k)) {
714                    k--;
715                }
716                // After this delete we need the new previous section, if there is one.
717                if (k < 0) {
718                    // There is no previous section
719                    prevSection = null;
720                } else {
721                    prevSection = sectionList.get(k);
722                    prevSectionDirection = direction.get(k);
723                }
724            }
725            removeSupportingArrayEntries(j);
726            initializeSectionCombos();
727        }
728        updateSeqNum();
729        sectionTableModel.fireTableDataChanged();
730    }
731
732    void insertAtBeginningPressed(ActionEvent e) {
733        if (insertAtBeginningBoxList.isEmpty()) {
734            JmriJOptionPane.showMessageDialog(addFrame, rbx
735                    .getString("Message35"), Bundle.getMessage("ErrorTitle"),
736                    JmriJOptionPane.ERROR_MESSAGE);
737            return;
738        }
739        int index = insertAtBeginningBox.getSelectedIndex();
740        Section s = insertAtBeginningBoxList.get(index);
741        if (s != null) {
742            sectionList.add(0, s);
743            direction.add(0, insertAtBeginningDirection.get(index));
744            curSequenceNum++;
745            sequence.add(0, 1);
746            alternate.add(0, false);
747            safe.add(0, addAsSafe.isSelected());
748            sensorStopAllocation.add(0, "");
749            action.add(0, new ArrayList<>());
750            fwdStopPerCent.add(0, Float.valueOf(1.0f));
751            revStopPerCent.add(0, Float.valueOf(1.0f));
752            if (curSequenceNum == 2) {
753                prevSectionDirection = direction.get(0);
754                prevSection = s;
755            }
756            initializeSectionCombos();
757        }
758        updateSeqNum();
759        sectionTableModel.fireTableDataChanged();
760    }
761
762    void removeFirstSectionPressed(ActionEvent e) {
763        if (curSequenceNum <= 1) {
764            deleteAllSections(e);
765        } else {
766            // For alternates we delete all
767            int keep = 1;
768            while (alternate.get(keep)) {
769                keep++;
770            }
771            for (int c = 0; c < keep ; c++) {
772                removeSupportingArrayEntries(0);
773                curSequenceNum--;
774            }
775            initializeSectionCombos();
776        }
777        updateSeqNum();
778        sectionTableModel.fireTableDataChanged();
779    }
780
781    void replacePrimaryForSeqPressed(ActionEvent e) {
782        int seq = getSeqNum();
783        if (seq == 0) {
784            return;
785        }
786        Section sOld = null;
787        List<Section> altOldList = new ArrayList<>();
788        Section beforeSection = null;
789        int beforeSectionDirection = 0;
790        Section afterSection = null;
791        int afterSectionDirection = 0;
792        int index = -1;
793        for (int i = 0; i < sectionList.size(); i++) {
794            if ((sequence.get(i) == seq) && (!alternate.get(i))) {
795                sOld = sectionList.get(i);
796                index = i;
797            }
798            if ((sequence.get(i) == seq) && alternate.get(i)) {
799                altOldList.add(sectionList.get(i));
800            }
801            if ((sequence.get(i) == (seq - 1)) && (!alternate.get(i))) {
802                beforeSection = sectionList.get(i);
803                beforeSectionDirection = direction.get(i);
804            }
805            if ((sequence.get(i) == (seq + 1)) && (!alternate.get(i))) {
806                afterSection = sectionList.get(i);
807                afterSectionDirection = Section.FORWARD;
808                if (afterSectionDirection == direction.get(i)) {
809                    afterSectionDirection = Section.REVERSE;
810                }
811            }
812        }
813        if (sOld == null) {
814            log.error("Missing primary Section for seq = {}", seq);
815            return;
816        }
817        List<Section> possibles = new ArrayList<>();
818        List<Integer> possiblesDirection = new ArrayList<>();
819        List<String> possibleNames = new ArrayList<>();
820
821        for (Section s : sectionManager.getNamedBeanSet()) {
822            Section mayBeSection = null;
823            String mayBeName = s.getDisplayName();
824            int mayBeDirection = 0;
825            if ((s != sOld) && (s != beforeSection)
826                    && (s != afterSection) && (!inSectionList(s, altOldList))) {
827                if (beforeSection != null) {
828                    if (forwardConnected(s, beforeSection, beforeSectionDirection)) {
829                        mayBeSection = s;
830                        mayBeDirection = Section.FORWARD;
831                    } else if (reverseConnected(s, beforeSection, beforeSectionDirection)) {
832                        mayBeSection = s;
833                        mayBeDirection = Section.REVERSE;
834                    }
835                    if ((mayBeSection != null) && (afterSection != null)) {
836                        if (mayBeDirection == Section.REVERSE) {
837                            if (!forwardConnected(s, afterSection, afterSectionDirection)) {
838                                mayBeSection = null;
839                            }
840                        } else {
841                            if (!reverseConnected(s, afterSection, afterSectionDirection)) {
842                                mayBeSection = null;
843                            }
844                        }
845                    }
846                } else if (afterSection != null) {
847                    if (forwardConnected(s, afterSection, afterSectionDirection)) {
848                        mayBeSection = s;
849                        mayBeDirection = Section.REVERSE;
850                    } else if (reverseConnected(s, afterSection, afterSectionDirection)) {
851                        mayBeSection = s;
852                        mayBeDirection = Section.FORWARD;
853                    }
854                } else {
855                    mayBeSection = s;
856                    mayBeDirection = Section.FORWARD;
857                }
858                if (mayBeSection != null) {
859                    possibles.add(mayBeSection);
860                    possiblesDirection.add(mayBeDirection);
861                    possibleNames.add(mayBeName);
862                }
863            }
864        }
865        if (possibles.isEmpty()) {
866            JmriJOptionPane.showMessageDialog(addFrame,
867                    java.text.MessageFormat.format(rbx.getString("Message36"),
868                            new Object[]{"" + seq}), Bundle.getMessage("ErrorTitle"),
869                    JmriJOptionPane.ERROR_MESSAGE);
870            return;
871        }
872        int k = 0;
873        if (possibles.size() > 1) {
874            Object choices[] = new Object[possibles.size()];
875            for (int j = 0; j < possibles.size(); j++) {
876                choices[j] = possibleNames.get(j);
877            }
878            Object selName = JmriJOptionPane.showInputDialog(addFrame,
879                    rbx.getString("ReplacePrimaryChoice"),
880                    rbx.getString("ReplacePrimaryTitle"),
881                    JmriJOptionPane.QUESTION_MESSAGE, null, choices, choices[0]);
882            if (selName == null) {
883                return;
884            }
885            for (int j = 0; j < possibles.size(); j++) {
886                if (selName.equals(choices[j])) {
887                    k = j;
888                }
889            }
890        }
891        sectionList.remove(index);
892        sectionList.add(index, possibles.get(k));
893        direction.set(index, possiblesDirection.get(k));
894        if (index == (sectionList.size() - 1)) {
895            curSection = sectionList.get(index);
896            curSectionDirection = direction.get(index);
897        } else if (index == (sectionList.size() - 2)) {
898            prevSection = sectionList.get(index);
899            prevSectionDirection = direction.get(index);
900        }
901        initializeSectionCombos();
902        sectionTableModel.fireTableDataChanged();
903    }
904
905    boolean inSectionList(Section s, List<Section> sList) {
906        for (int i = 0; i < sList.size(); i++) {
907            if (sList.get(i) == s) {
908                return true;
909            }
910        }
911        return false;
912    }
913
914    private int getSeqNum() {
915        int n = (Integer) seqNum.getValue(); // JSpinner int from 1 - sectionList.size()
916        if (n > curSequenceNum) {
917            JmriJOptionPane.showMessageDialog(null, rbx
918                    .getString("Message34"), Bundle.getMessage("ErrorTitle"),
919                    JmriJOptionPane.ERROR_MESSAGE);
920            return 0;
921        }
922        return n;
923    }
924
925    /**
926     * After any add, delete etc the section sequence numbers need to be
927     * rebuilt.
928     * After which we update sequence Number spinner on pane.
929     * Limit spinner to highest sequence index in
930     * section table (column 0).
931     */
932    void updateSeqNum() {
933        int seqMax = 0;
934        int seqNumber = 0;
935        for (int ix = 0; ix<alternate.size();ix++) {
936            if (!alternate.get(ix)) {
937                seqNumber++;
938            }
939            sequence.set(ix,seqNumber);
940        }
941        seqMax = seqNumber;
942        seqNum.setModel(new SpinnerNumberModel(
943                seqMax, // initial value set
944                Math.min(seqMax, 1), // minimum value, either 0 (empty list) or 1
945                seqMax, // maximum order number
946                1));
947        seqNum.setValue(Math.min(seqMax, 1));
948    }
949
950    void deleteAlternateForSeqPressed(ActionEvent e) {
951        if (sectionList.size() <= 1) {
952            deleteAllSections(e);
953        } else {
954            int seq = getSeqNum();
955            if (seq == 0) {
956                return;
957            }
958            for (int i = sectionList.size() - 1; i >= seq; i--) {
959                if ((sequence.get(i) == seq) && alternate.get(i)) {
960                    removeSupportingArrayEntries(i);
961                }
962            }
963            initializeSectionCombos();
964        }
965        updateSeqNum();
966        sectionTableModel.fireTableDataChanged();
967    }
968
969    void addAlternateForSeqPressed(ActionEvent e) {
970        int seq = getSeqNum();
971        if (seq == 0) {
972            return;
973        }
974        Section primarySection = null;
975        List<Section> altOldList = new ArrayList<>();
976        Section beforeSection = null;
977        int beforeSectionDirection = 0;
978        Section afterSection = null;
979        int afterSectionDirection = 0;
980        int index = -1;
981        for (int i = 0; i < sectionList.size(); i++) {
982            if ((sequence.get(i) == seq) && (!alternate.get(i))) {
983                primarySection = sectionList.get(i);
984                index = i;
985            }
986            if ((sequence.get(i) == seq) && alternate.get(i)) {
987                altOldList.add(sectionList.get(i));
988            }
989            if ((sequence.get(i) == (seq - 1)) && (!alternate.get(i))) {
990                beforeSection = sectionList.get(i);
991                beforeSectionDirection = direction.get(i);
992            }
993            if ((sequence.get(i) == (seq + 1)) && (!alternate.get(i))) {
994                afterSection = sectionList.get(i);
995                afterSectionDirection = Section.FORWARD;
996                if (afterSectionDirection == direction.get(i)) {
997                    afterSectionDirection = Section.REVERSE;
998                }
999            }
1000        }
1001        if (primarySection == null) {
1002            log.error("Missing primary Section for seq = {}", seq);
1003            return;
1004        }
1005        List<Section> possibles = new ArrayList<>();
1006        List<Integer> possiblesDirection = new ArrayList<>();
1007        List<String> possibleNames = new ArrayList<>();
1008        for (Section s : sectionManager.getNamedBeanSet()) {
1009            Section mayBeSection = null;
1010            String mayBeName = s.getDisplayName();
1011            int mayBeDirection = 0;
1012            if ((s != primarySection) && (s != beforeSection)
1013                    && (s != afterSection) && (!inSectionList(s, altOldList))) {
1014                if (beforeSection != null) {
1015                    if (forwardConnected(s, beforeSection, beforeSectionDirection)) {
1016                        mayBeSection = s;
1017                        mayBeDirection = Section.FORWARD;
1018                    } else if (reverseConnected(s, beforeSection, beforeSectionDirection)) {
1019                        mayBeSection = s;
1020                        mayBeDirection = Section.REVERSE;
1021                    }
1022                    if ((mayBeSection != null) && (afterSection != null)) {
1023                        if (mayBeDirection == Section.REVERSE) {
1024                            if (!forwardConnected(s, afterSection, afterSectionDirection)) {
1025                                mayBeSection = null;
1026                            }
1027                        } else {
1028                            if (!reverseConnected(s, afterSection, afterSectionDirection)) {
1029                                mayBeSection = null;
1030                            }
1031                        }
1032                    }
1033                } else if (afterSection != null) {
1034                    if (forwardConnected(s, afterSection, afterSectionDirection)) {
1035                        mayBeSection = s;
1036                        mayBeDirection = Section.REVERSE;
1037                    } else if (reverseConnected(s, afterSection, afterSectionDirection)) {
1038                        mayBeSection = s;
1039                        mayBeDirection = Section.FORWARD;
1040                    }
1041                } else {
1042                    mayBeSection = s;
1043                    mayBeDirection = Section.FORWARD;
1044                }
1045                if (mayBeSection != null) {
1046                    possibles.add(mayBeSection);
1047                    possiblesDirection.add(mayBeDirection);
1048                    possibleNames.add(mayBeName);
1049                }
1050            }
1051        }
1052        if (possibles.isEmpty()) {
1053            JmriJOptionPane.showMessageDialog(addFrame,
1054                    java.text.MessageFormat.format(rbx.getString("Message37"),
1055                            new Object[]{"" + seq}), Bundle.getMessage("ErrorTitle"),
1056                    JmriJOptionPane.ERROR_MESSAGE);
1057            return;
1058        }
1059        int k = 0;
1060        if (possibles.size() > 1) {
1061            Object choices[] = new Object[possibles.size()];
1062            for (int j = 0; j < possibles.size(); j++) {
1063                choices[j] = possibleNames.get(j);
1064            }
1065            Object selName = JmriJOptionPane.showInputDialog(addFrame,
1066                    rbx.getString("AddAlternateChoice"),
1067                    rbx.getString("AddAlternateTitle"),
1068                    JmriJOptionPane.QUESTION_MESSAGE, null, choices, choices[0]);
1069            if (selName == null) {
1070                return;
1071            }
1072            for (int j = 0; j < possibles.size(); j++) {
1073                if (selName.equals(choices[j])) {
1074                    k = j;
1075                }
1076            }
1077        }
1078        index = index + 1 + altOldList.size();
1079        sectionList.add(index, possibles.get(k));
1080        direction.add(index, possiblesDirection.get(k));
1081        sequence.add(index, sequence.get(index - 1));
1082        alternate.add(index, true);
1083        safe.add(index, addAsSafe.isSelected());
1084        if (stopAllocatingSensorBox.getSelectedIndex() < 0) {
1085            sensorStopAllocation.add(index, "");
1086        } else {
1087            sensorStopAllocation.add(index, (String) stopAllocatingSensorBox.getSelectedItem());
1088        }
1089        if (stopAllocatingSensorBox.getSelectedIndex() < 0) {
1090            sensorStopAllocation.add(index, "");
1091        } else {
1092            sensorStopAllocation.add(index, (String) stopAllocatingSensorBox.getSelectedItem());
1093        }
1094        fwdStopPerCent.add(index, Float.valueOf(1.0f));
1095        revStopPerCent.add(index, Float.valueOf(1.0f));
1096        action.add(index, new ArrayList<>());
1097        initializeSectionCombos();
1098        updateSeqNum();
1099        sectionTableModel.fireTableDataChanged();
1100    }
1101
1102    void addAlternateSectionPressed(ActionEvent e) {
1103        if (alternateSectionBoxList.isEmpty()) {
1104            JmriJOptionPane.showMessageDialog(addFrame, rbx
1105                    .getString("Message24"), Bundle.getMessage("ErrorTitle"),
1106                    JmriJOptionPane.ERROR_MESSAGE);
1107            return;
1108        }
1109        int index = alternateSectionBox.getSelectedIndex();
1110        Section s = alternateSectionBoxList.get(index);
1111        if (s != null) {
1112            sectionList.add(s);
1113            direction.add(altSectionDirection.get(index));
1114            sequence.add(curSequenceNum);
1115            action.add(new ArrayList<>());
1116            alternate.add(true);
1117            safe.add(addAsSafe.isSelected());
1118            sensorStopAllocation.add((String)stopAllocatingSensorBox.getSelectedItem());
1119            fwdStopPerCent.add( Float.valueOf(1.0f));
1120            revStopPerCent.add( Float.valueOf(1.0f));
1121            initializeSectionCombos();
1122        }
1123        updateSeqNum();
1124        sectionTableModel.fireTableDataChanged();
1125    }
1126
1127    void createPressed(ActionEvent e) {
1128        if (!checkTransitInformation()) {
1129            return;
1130        }
1131        String uName = userName.getText();
1132        if (uName.isEmpty()) {
1133            uName = null;
1134        }
1135
1136        try {
1137            // attempt to create the new Transit
1138            if (_autoSystemName.isSelected()) {
1139                curTransit = transitManager.createNewTransit(uName);
1140            } else {
1141                String sName = sysName.getText();
1142                curTransit = transitManager.createNewTransit(sName, uName);
1143            }
1144        } catch (IllegalArgumentException ex) {
1145            JmriJOptionPane.showMessageDialog(addFrame, ex.getLocalizedMessage(), Bundle.getMessage("ErrorTitle"),
1146                    JmriJOptionPane.ERROR_MESSAGE);
1147            return;
1148        }
1149        sysName.setText(curTransit.getSystemName());
1150        setTransitInformation();
1151        addFrame.setVisible(false);
1152        pref.setSimplePreferenceState(systemNameAuto, _autoSystemName.isSelected());
1153    }
1154
1155    void cancelPressed(ActionEvent e) {
1156        addFrame.setVisible(false);
1157        sectionTableModel.dispose();
1158        addFrame.dispose();  // remove addFrame from Windows menu
1159        addFrame = null;
1160    }
1161
1162    void updatePressed(ActionEvent e) {
1163        if (!checkTransitInformation()) {
1164            return;
1165        }
1166        // check if user name has been changed
1167        String uName = userName.getText();
1168        if (uName.isEmpty()) {
1169            uName = null;
1170        }
1171        if ((uName != null) && (!uName.equals(curTransit.getUserName()))) {
1172            // check that new user name is unique
1173            Transit tTransit = transitManager.getByUserName(uName);
1174            if (tTransit != null) {
1175                JmriJOptionPane.showMessageDialog(addFrame, rbx
1176                        .getString("Message22"), Bundle.getMessage("ErrorTitle"),
1177                        JmriJOptionPane.ERROR_MESSAGE);
1178                return;
1179            }
1180        }
1181        curTransit.setUserName(uName);
1182        if (setTransitInformation()) {
1183            // successful update
1184            addFrame.setVisible(false);
1185            sectionTableModel.dispose();
1186            addFrame.dispose();  // remove addFrame from Windows menu
1187            addFrame = null;
1188        }
1189    }
1190    
1191    private void removeSupportingArrayEntries(int index) {
1192        sectionList.remove(index);
1193        sequence.remove(index);
1194        direction.remove(index);
1195        action.remove(index);
1196        alternate.remove(index);
1197        safe.remove(index);
1198        sensorStopAllocation.remove(index);
1199        fwdStopPerCent.remove(index);
1200        revStopPerCent.remove(index);
1201    }
1202
1203    private boolean checkTransitInformation() {
1204        //transits can now be of length 1 segmant.
1205        //With these the route has to start outside the transit
1206        if ((sectionList.size() < 1) || (curSequenceNum < 1)) {
1207            JmriJOptionPane.showMessageDialog(addFrame, rbx
1208                    .getString("Message26"), Bundle.getMessage("ErrorTitle"),
1209                    JmriJOptionPane.ERROR_MESSAGE);
1210            return false;
1211        }
1212        return true;
1213    }
1214
1215    private boolean setTransitInformation() {
1216        if (curTransit == null) {
1217            return false;
1218        }
1219        curTransit.removeAllSections();
1220        for (int i = 0; i < sectionList.size(); i++) {
1221            TransitSection ts = new TransitSection(sectionList.get(i),
1222                    sequence.get(i), direction.get(i), alternate.get(i), safe.get(i),
1223                    sensorStopAllocation.get(i), fwdStopPerCent.get(i), revStopPerCent.get(i));
1224            List<TransitSectionAction> list = action.get(i);
1225            if (list != null) {
1226                for (int j = 0; j < list.size(); j++) {
1227                    ts.addAction(list.get(j));
1228                }
1229            }
1230            curTransit.addTransitSection(ts);
1231        }
1232        return true;
1233    }
1234
1235    private void initializeSectionCombos() {
1236        primarySectionBox.removeAllItems();
1237        alternateSectionBox.removeAllItems();
1238        insertAtBeginningBox.removeAllItems();
1239        primarySectionBoxList.clear();
1240        alternateSectionBoxList.clear();
1241        insertAtBeginningBoxList.clear();
1242        priSectionDirection.clear();
1243        altSectionDirection.clear();
1244        insertAtBeginningDirection.clear();
1245        if (sectionList.isEmpty()) {
1246            // no Sections currently in Transit - all Sections and all Directions OK
1247            for (Section s : sectionManager.getNamedBeanSet()) {
1248                String sName = s.getDisplayName();
1249                primarySectionBox.addItem(sName);
1250                primarySectionBoxList.add(s);
1251                priSectionDirection.add(Section.FORWARD);
1252            }
1253        } else {
1254            // limit to Sections that connect to the current Section and are not the previous Section
1255            for (Section s : sectionManager.getNamedBeanSet()) {
1256                String sName = s.getDisplayName();
1257                if ((s != prevSection) && (forwardConnected(s, curSection, curSectionDirection))) {
1258                    primarySectionBox.addItem(sName);
1259                    primarySectionBoxList.add(s);
1260                    priSectionDirection.add(Section.FORWARD);
1261                } else if ((s != prevSection) && (reverseConnected(s, curSection, curSectionDirection))) {
1262                    primarySectionBox.addItem(sName);
1263                    primarySectionBoxList.add(s);
1264                    priSectionDirection.add(Section.REVERSE);
1265                }
1266            }
1267            // check if there are any alternate Section choices
1268            if (prevSection != null) {
1269                for (Section s : sectionManager.getNamedBeanSet()) {
1270                    String sName = s.getDisplayName();
1271                    if ((notIncludedWithSeq(s, curSequenceNum))
1272                            && forwardConnected(s, prevSection, prevSectionDirection)) {
1273                        alternateSectionBox.addItem(sName);
1274                        alternateSectionBoxList.add(s);
1275                        altSectionDirection.add( Section.FORWARD);
1276                    } else if (notIncludedWithSeq(s, curSequenceNum)
1277                            && reverseConnected(s, prevSection, prevSectionDirection)) {
1278                        alternateSectionBox.addItem(sName);
1279                        alternateSectionBoxList.add(s);
1280                        altSectionDirection.add(Section.REVERSE);
1281                    }
1282                }
1283            }
1284            // check if there are any Sections available to be inserted at beginning
1285            Section firstSection = sectionList.get(0);
1286            int testDirection = Section.FORWARD;
1287            if (direction.get(0) == Section.FORWARD) {
1288                testDirection = Section.REVERSE;
1289            }
1290            for (Section s : sectionManager.getNamedBeanSet()) {
1291                String sName = s.getDisplayName();
1292                if ((s != firstSection) && (forwardConnected(s, firstSection, testDirection))) {
1293                    insertAtBeginningBox.addItem(sName);
1294                    insertAtBeginningBoxList.add(s);
1295                    insertAtBeginningDirection.add( Section.REVERSE);
1296                } else if ((s != firstSection) && (reverseConnected(s, firstSection, testDirection))) {
1297                    insertAtBeginningBox.addItem(sName);
1298                    insertAtBeginningBoxList.add(s);
1299                    insertAtBeginningDirection.add( Section.FORWARD);
1300                }
1301            }
1302        }
1303        JComboBoxUtil.setupComboBoxMaxRows(primarySectionBox);
1304        JComboBoxUtil.setupComboBoxMaxRows(alternateSectionBox);
1305        JComboBoxUtil.setupComboBoxMaxRows(insertAtBeginningBox);
1306    }
1307
1308    private boolean forwardConnected(Section s1, Section s2, int restrictedDirection) {
1309        if ((s1 != null) && (s2 != null)) {
1310            List<EntryPoint> s1ForwardEntries = s1.getForwardEntryPointList();
1311            List<EntryPoint> s2Entries;
1312            switch (restrictedDirection) {
1313                case Section.FORWARD:
1314                    s2Entries = s2.getReverseEntryPointList();
1315                    break;
1316                case Section.REVERSE:
1317                    s2Entries = s2.getForwardEntryPointList();
1318                    break;
1319                default:
1320                    s2Entries = s2.getEntryPointList();
1321                    break;
1322            }
1323            for (int i = 0; i < s1ForwardEntries.size(); i++) {
1324                Block b1 = s1ForwardEntries.get(i).getFromBlock();
1325                for (int j = 0; j < s2Entries.size(); j++) {
1326                    Block b2 = s2Entries.get(j).getFromBlock();
1327                    if ((b1 == s2Entries.get(j).getBlock())
1328                            && (b2 == s1ForwardEntries.get(i).getBlock())) {
1329                        return true;
1330                    }
1331                }
1332            }
1333        }
1334        return false;
1335    }
1336
1337    private boolean reverseConnected(Section s1, Section s2, int restrictedDirection) {
1338        if ((s1 != null) && (s2 != null)) {
1339            List<EntryPoint> s1ReverseEntries = s1.getReverseEntryPointList();
1340            List<EntryPoint> s2Entries;
1341            switch (restrictedDirection) {
1342                case Section.FORWARD:
1343                    s2Entries = s2.getReverseEntryPointList();
1344                    break;
1345                case Section.REVERSE:
1346                    s2Entries = s2.getForwardEntryPointList();
1347                    break;
1348                default:
1349                    s2Entries = s2.getEntryPointList();
1350                    break;
1351            }
1352            for (int i = 0; i < s1ReverseEntries.size(); i++) {
1353                Block b1 = s1ReverseEntries.get(i).getFromBlock();
1354                for (int j = 0; j < s2Entries.size(); j++) {
1355                    Block b2 = s2Entries.get(j).getFromBlock();
1356                    if ((b1 == s2Entries.get(j).getBlock())
1357                            && (b2 == s1ReverseEntries.get(i).getBlock())) {
1358                        return true;
1359                    }
1360                }
1361            }
1362        }
1363        return false;
1364    }
1365
1366    private boolean notIncludedWithSeq(Section s, int seq) {
1367        for (int i = 0; i < sectionList.size(); i++) {
1368            if ((sectionList.get(i) == s) && (seq == sequence.get(i))) {
1369                return false;
1370            }
1371        }
1372        return true;
1373    }
1374
1375    private void autoSystemName() {
1376        if (_autoSystemName.isSelected()) {
1377//            create.setEnabled(true);
1378            sysName.setEnabled(false);
1379            sysNameLabel.setEnabled(false);
1380        } else {
1381//            if (sysName.getText().length() > 0)
1382//                create.setEnabled(true);
1383//            else
1384//                create.setEnabled(false);
1385            sysName.setEnabled(true);
1386            sysNameLabel.setEnabled(true);
1387        }
1388    }
1389
1390    // variables for View Actions window
1391    private int activeRow = 0;
1392    private SpecialActionTableModel actionTableModel = null;
1393    private JmriJFrame actionTableFrame = null;
1394    private final JLabel fixedSectionLabel = new JLabel("X");
1395
1396    private void addEditActionsPressed(int r) {
1397        activeRow = r;
1398        if (actionTableModel != null) {
1399            actionTableModel.fireTableStructureChanged();
1400        }
1401        if (actionTableFrame == null) {
1402            actionTableFrame = new JmriJFrame(rbx.getString("TitleViewActions"));
1403            actionTableFrame.addHelpMenu(
1404                    "package.jmri.jmrit.beantable.ViewSpecialActions", true);
1405            Container contentPane = actionTableFrame.getContentPane();
1406            contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
1407            JPanel panel1 = new JPanel();
1408            panel1.setLayout(new FlowLayout());
1409            JLabel sectionNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameSection")));
1410            panel1.add(sectionNameLabel);
1411            panel1.add(fixedSectionLabel);
1412            contentPane.add(panel1);
1413            addFrame.getContentPane().add(new JSeparator());
1414            JPanel pct = new JPanel();
1415            pct.setLayout(new BorderLayout());
1416            // initialize table of actions
1417            actionTableModel = new SpecialActionTableModel();
1418            JTable actionTable = new JTable(actionTableModel);
1419            actionTable.setRowSelectionAllowed(false);
1420            TableColumnModel actionColumnModel = actionTable
1421                    .getColumnModel();
1422            TableColumn whenColumn = actionColumnModel
1423                    .getColumn(SpecialActionTableModel.WHEN_COLUMN);
1424            whenColumn.setResizable(true);
1425            TableColumn whatColumn = actionColumnModel
1426                    .getColumn(SpecialActionTableModel.WHAT_COLUMN);
1427            whatColumn.setResizable(true);
1428            TableColumn editColumn = actionColumnModel
1429                    .getColumn(SpecialActionTableModel.EDIT_COLUMN);
1430            // install button renderer and editor
1431            ButtonRenderer buttonRenderer = new ButtonRenderer();
1432            actionTable.setDefaultRenderer(JButton.class, buttonRenderer);
1433            TableCellEditor buttonEditor = new ButtonEditor(new JButton());
1434            actionTable.setDefaultEditor(JButton.class, buttonEditor);
1435            JButton testButton = new JButton(Bundle.getMessage("ButtonDelete"));
1436            actionTable.setRowHeight(testButton.getPreferredSize().height);
1437            editColumn.setResizable(false);
1438            editColumn.setMinWidth(testButton.getPreferredSize().width);
1439            editColumn.setMaxWidth(testButton.getPreferredSize().width);
1440            TableColumn removeColumn = actionColumnModel
1441                    .getColumn(SpecialActionTableModel.REMOVE_COLUMN);
1442            removeColumn.setMinWidth(testButton.getPreferredSize().width);
1443            removeColumn.setMaxWidth(testButton.getPreferredSize().width);
1444            removeColumn.setResizable(false);
1445            JScrollPane actionTableScrollPane = new JScrollPane(
1446                    actionTable);
1447            pct.add(actionTableScrollPane, BorderLayout.CENTER);
1448            contentPane.add(pct);
1449            pct.setVisible(true);
1450            // add View Action panel buttons
1451            JPanel but = new JPanel();
1452            but.setLayout(new BoxLayout(but, BoxLayout.Y_AXIS));
1453            JPanel panel4 = new JPanel();
1454            panel4.setLayout(new FlowLayout());
1455            JButton newActionButton = new JButton(rbx.getString("ButtonAddNewAction"));
1456            panel4.add(newActionButton);
1457            newActionButton.addActionListener(this::newActionPressed);
1458            newActionButton.setToolTipText(rbx.getString("NewActionButtonHint"));
1459            JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));
1460            panel4.add(doneButton);
1461            doneButton.addActionListener(this::doneWithActionsPressed);
1462            doneButton.setToolTipText(rbx.getString("DoneButtonHint"));
1463            but.add(panel4);
1464            contentPane.add(but);
1465        }
1466        fixedSectionLabel.setText(getSectionNameByRow(r) + "    "
1467                + rbx.getString("SequenceAbbrev") + ": " + sequence.get(r));
1468        actionTableFrame.addWindowListener(new java.awt.event.WindowAdapter() {
1469            @Override
1470            public void windowClosing(java.awt.event.WindowEvent e) {
1471                actionTableFrame.setVisible(false);
1472                actionTableFrame.dispose();
1473                actionTableFrame = null;
1474                if (addEditActionFrame != null) {
1475                    addEditActionFrame.setVisible(false);
1476                    addEditActionFrame.dispose();
1477                    addEditActionFrame = null;
1478                }
1479            }
1480        });
1481        actionTableFrame.pack();
1482        actionTableFrame.setVisible(true);
1483    }
1484
1485    private void doneWithActionsPressed(ActionEvent e) {
1486        actionTableFrame.setVisible(false);
1487        actionTableFrame.dispose();
1488        actionTableFrame = null;
1489        if (addEditActionFrame != null) {
1490            addEditActionFrame.setVisible(false);
1491            addEditActionFrame.dispose();
1492            addEditActionFrame = null;
1493        }
1494    }
1495
1496    private void newActionPressed(ActionEvent e) {
1497        editActionMode = false;
1498        curTSA = null;
1499        addEditActionWindow();
1500    }
1501
1502    // variables for Add/Edit Action window
1503    private boolean editActionMode = false;
1504    private JmriJFrame addEditActionFrame = null;
1505    private TransitSectionAction curTSA = null;
1506    private final JComboBox<String> whenBox = new JComboBox<>();
1507    private final NamedBeanComboBox<Sensor> whenSensorComboBox = new NamedBeanComboBox<>(
1508        InstanceManager.getDefault(SensorManager.class), null, DisplayOptions.DISPLAYNAME);
1509    private final JSpinner whenDataSpinnerFloat = new JSpinner(new SpinnerNumberModel(
1510        Float.valueOf(0.0f), Float.valueOf(0.0f), Float.valueOf(65.0f), Float.valueOf(0.5f))); // delay
1511    private final JSpinner whenDataSpinnerInt = new JSpinner(new SpinnerNumberModel(0, 0, 65000, 100)); // delay
1512    private final JRadioButton mSecButton = new JRadioButton(Bundle.getMessage("LabelMilliseconds"));
1513    private final JRadioButton secButton = new JRadioButton(Bundle.getMessage("LabelSeconds"));
1514    private final JComboBox<String> whatBox = new JComboBox<>();
1515    private final JSpinner whatPercentSpinner = new JSpinner(); // speed
1516    private final JSpinner whatMinuteSpinner1 = new JSpinner(new SpinnerNumberModel(1, 1, 65500, 1));     // time in ms
1517    private final JSpinner whatMinuteSpinner2 = new JSpinner(new SpinnerNumberModel(100, 100, 65500, 1)); // time in ms
1518    private final JSpinner locoFunctionSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 28, 1));       // function ID
1519    private final JTextField whatStringField = new JTextField(12);
1520    private final JTextField locoAddress = new JTextField(12);
1521    private final RosterEntryComboBox rosterComboBox = new RosterEntryComboBox();
1522    private final JComboBox<String> trainInfoComboBox = new JComboBox<>();
1523    private final JRadioButton locoAddressDefault = new JRadioButton(rbx.getString("TrainInfoUseDefault"));
1524    private final JRadioButton locoAddressRoster = new JRadioButton(rbx.getString("TrainInfoUseRoster"));
1525    private final JRadioButton locoAddressNumber = new JRadioButton(rbx.getString("TrainInfoUseAddress"));
1526    private final JRadioButton locoAddressCurrent = new JRadioButton(rbx.getString("TrainInfoUseCurrentAddress"));
1527    private final ButtonGroup locoAddressGroup = new ButtonGroup();
1528    private JButton updateActionButton = null;
1529    private JButton createActionButton = null;
1530    private JButton cancelAddEditActionButton = null;
1531    private final JComboBox<String> blockBox = new JComboBox<>();
1532    private List<Block> blockList = new ArrayList<>();
1533    private final JRadioButton onButton = new JRadioButton(Bundle.getMessage("StateOn"));
1534    private final JRadioButton offButton = new JRadioButton(Bundle.getMessage("StateOff"));
1535    private final JLabel doneSensorLabel = new JLabel(rbx.getString("DoneSensorLabel"));
1536    private JPanel signalPanel;
1537    private JPanel panelPercentageSpinner;
1538    private JPanel panelDelay;
1539    private final JLabel panelDelayLabel = new JLabel();
1540    private JPanel panelWhatBox;
1541    private JPanel panelLoadTrainInfo;
1542    private final NamedBeanComboBox<Sensor> doneSensorComboBox = new NamedBeanComboBox<>(
1543        InstanceManager.getDefault(SensorManager.class), null, DisplayOptions.DISPLAYNAME);
1544    private final NamedBeanComboBox<SignalMast> signalMastComboBox = new NamedBeanComboBox<>(
1545        InstanceManager.getDefault(SignalMastManager.class), null, DisplayOptions.DISPLAYNAME);
1546    private final NamedBeanComboBox<SignalHead> signalHeadComboBox = new NamedBeanComboBox<>(
1547        InstanceManager.getDefault(SignalHeadManager.class), null, DisplayOptions.DISPLAYNAME);
1548
1549    private void addEditActionWindow() {
1550        if (addEditActionFrame == null) {
1551            // set up add/edit action window
1552            addEditActionFrame = new JmriJFrame(rbx.getString("TitleAddAction"));
1553            addEditActionFrame.addHelpMenu(
1554                    "package.jmri.jmrit.beantable.TransitSectionAddEditAction", true);
1555            Container contentPane = addEditActionFrame.getContentPane();
1556            contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
1557            // to set When to start the action
1558            JPanel panelx = new JPanel();
1559            panelx.setLayout(new BoxLayout(panelx, BoxLayout.Y_AXIS));
1560            JPanel panel1 = new JPanel();
1561            panel1.setLayout(new FlowLayout());
1562            panel1.add(new JLabel(rbx.getString("WhenText")));
1563            initializeWhenBox();
1564            JComboBoxUtil.setupComboBoxMaxRows(whenBox);
1565            panel1.add(whenBox);
1566            whenBox.addActionListener((ActionEvent e) -> {
1567                log.debug("whenBox was set");
1568                if (whenBox.getSelectedItem()!=null) {
1569                    setWhen(getWhenMenuCode((String)whenBox.getSelectedItem()));
1570                }
1571            });
1572            whenBox.setToolTipText(rbx.getString("WhenBoxTip"));
1573            JComboBoxUtil.setupComboBoxMaxRows(whenSensorComboBox);
1574            panel1.add(whenSensorComboBox);
1575            whenSensorComboBox.setAllowNull(true);
1576            initializeBlockBox();
1577            JComboBoxUtil.setupComboBoxMaxRows(blockBox);
1578            panel1.add(blockBox);
1579            panelx.add(panel1);
1580            // to set optional delay setting
1581            panelDelay = new JPanel();
1582            panelDelay.setLayout(new FlowLayout());
1583            panelDelayLabel.setText("    " + rbx.getString("OptionalDelay") + ": ");
1584            panelDelay.add(panelDelayLabel);
1585            panelDelay.add(whenDataSpinnerInt);
1586            whenDataSpinnerInt.setToolTipText(rbx.getString("HintDelayData"));
1587            whenDataSpinnerInt.addChangeListener((ChangeEvent e) -> {
1588                if (mSecButton.isSelected()) {
1589                    float fl = (int)whenDataSpinnerInt.getValue();
1590                    whenDataSpinnerFloat.setValue(fl/1000.0f);
1591                }
1592            });
1593            panelDelay.add(whenDataSpinnerFloat);
1594            whenDataSpinnerFloat.setToolTipText(rbx.getString("HintDelayData"));
1595            whenDataSpinnerFloat.setPreferredSize(whenDataSpinnerInt.getPreferredSize());
1596            whenDataSpinnerFloat.addChangeListener( e -> {
1597                if (secButton.isSelected()) {
1598                    float dVal = (float)whenDataSpinnerFloat.getValue();
1599                    dVal *= 1000.0;
1600                    whenDataSpinnerInt.setValue(Math.round(dVal));
1601                }
1602            });
1603            ButtonGroup secMsec = new ButtonGroup();
1604            secMsec.add(mSecButton);
1605            secMsec.add(secButton);
1606            panelDelay.add(mSecButton);
1607            mSecButton.addChangeListener( e -> {
1608                if (mSecButton.isSelected()) {
1609                    whenDataSpinnerFloat.setVisible(false);
1610                    whenDataSpinnerInt.setVisible(true);
1611                }
1612            });
1613            panelDelay.add(secButton);
1614            secButton.addChangeListener( e -> {
1615                if (secButton.isSelected()) {
1616                    whenDataSpinnerFloat.setVisible(true);
1617                    whenDataSpinnerInt.setVisible(false);
1618                }
1619            });
1620            secButton.setSelected(true);
1621            panelx.add(panelDelay);
1622            JPanel spacer = new JPanel();
1623            spacer.setLayout(new FlowLayout());
1624            spacer.add(new JLabel("     "));
1625            panelx.add(spacer);
1626            // to set What action to take
1627            panelWhatBox = new JPanel();
1628            panelWhatBox.setLayout(new FlowLayout());
1629            panelWhatBox.add(new JLabel(rbx.getString("WhatText")));
1630            initializeWhatBox(0);
1631            JComboBoxUtil.setupComboBoxMaxRows(whatBox);
1632            panelWhatBox.add(whatBox);
1633            whatBox.setToolTipText(rbx.getString("WhatBoxTip"));
1634            whatBox.addActionListener( e -> {
1635                if (whatBox.getSelectedItem()!=null) {
1636                    setWhat(getWhatMenuCode((String)whatBox.getSelectedItem()));
1637                }
1638            });
1639            panelWhatBox.add(whatStringField);
1640            whatStringField.setToolTipText(rbx.getString("HintSoundHornPatternString"));
1641            panelx.add(panelWhatBox);
1642
1643            // Train Info
1644            TitledBorder trainInfoBorder = BorderFactory.createTitledBorder(rbx.getString("SelectTrain"));
1645            panelLoadTrainInfo = new JPanel();
1646            panelLoadTrainInfo.setLayout(new BoxLayout(panelLoadTrainInfo, BoxLayout.Y_AXIS));
1647            panelLoadTrainInfo.setBorder(trainInfoBorder);
1648            JPanel panelUseInfo = new JPanel();
1649            panelUseInfo.setBorder(trainInfoBorder);
1650            panelUseInfo.add(new JLabel(rbx.getString("TrainInfoFile")));
1651            panelUseInfo.add(trainInfoComboBox);
1652            trainInfoComboBox.setToolTipText(rbx.getString("HintTrainInfoFile"));
1653            panelLoadTrainInfo.add(panelUseInfo);
1654            TitledBorder useLocoBorder = BorderFactory.createTitledBorder(rbx.getString("SelectALoco"));
1655            JPanel panelUseLoco = new JPanel();
1656            panelUseLoco.setBorder(useLocoBorder);
1657
1658            locoAddressGroup.add(locoAddressDefault);
1659            locoAddressDefault.setToolTipText(rbx.getString("TrainInfoUseDefaultHint"));
1660            locoAddressDefault.addActionListener(this::updateTrainInfoAddressFields);
1661            panelUseLoco.add(locoAddressDefault);
1662            locoAddressGroup.add(locoAddressCurrent);
1663            locoAddressCurrent.setToolTipText(rbx.getString("TrainInfoUseCurrentAddressHint"));
1664            locoAddressCurrent.addActionListener(this::updateTrainInfoAddressFields);
1665            panelUseLoco.add(locoAddressCurrent);
1666            locoAddressGroup.add(locoAddressRoster);
1667            locoAddressRoster.addActionListener(this::updateTrainInfoAddressFields);
1668            panelUseLoco.add(locoAddressRoster);
1669            locoAddressGroup.add(locoAddressNumber);
1670            locoAddressNumber.addActionListener(this::updateTrainInfoAddressFields);
1671            panelUseLoco.add(locoAddressNumber);
1672            panelUseLoco.add(locoAddress);
1673            locoAddress.setToolTipText(rbx.getString("HintLocoMotiveAddress"));
1674            panelUseLoco.add(rosterComboBox);
1675            panelLoadTrainInfo.add(panelUseLoco);
1676            panelx.add(panelLoadTrainInfo);
1677            panelLoadTrainInfo.setVisible(false);
1678
1679            panelPercentageSpinner = new JPanel();
1680            panelPercentageSpinner.setLayout(new FlowLayout());
1681            whatPercentSpinner.setModel(new SpinnerNumberModel(
1682                Float.valueOf(1.0f), Float.valueOf(0.00f), Float.valueOf(1.5f), Float.valueOf(0.01f)));
1683            whatPercentSpinner.setEditor(new JSpinner.NumberEditor(whatPercentSpinner, "# %")); // show as a percentage % sign
1684            panelPercentageSpinner.add(whatPercentSpinner);
1685            panelPercentageSpinner.add(whatMinuteSpinner1);
1686            panelPercentageSpinner.add(whatMinuteSpinner2);
1687            panelPercentageSpinner.add(locoFunctionSpinner);
1688            // signal comboboxes
1689            TitledBorder border = BorderFactory.createTitledBorder(rbx.getString("SelectASignal"));
1690            signalPanel = new JPanel();
1691            signalPanel.setBorder(border);
1692            signalPanel.add(new JLabel(rbx.getString("MastLabel")));
1693            JComboBoxUtil.setupComboBoxMaxRows(signalMastComboBox);
1694            signalPanel.add(signalMastComboBox);
1695            signalMastComboBox.setAllowNull(true);
1696            signalMastComboBox.addActionListener((ActionEvent e) -> {
1697                if (signalMastComboBox.getSelectedIndex() > 0) {
1698                    signalHeadComboBox.setSelectedIndex(-1); // choose either a head or a mast
1699                }
1700            });
1701            signalPanel.add(new JLabel(rbx.getString("HeadLabel")));
1702            JComboBoxUtil.setupComboBoxMaxRows(signalHeadComboBox);
1703            signalPanel.add(signalHeadComboBox);
1704            signalHeadComboBox.setAllowNull(true);
1705            signalHeadComboBox.addActionListener((ActionEvent e) -> {
1706                if (signalHeadComboBox.getSelectedIndex() > 0) {
1707                    signalMastComboBox.setSelectedIndex(-1); // choose either a head or a mast
1708                }
1709            });
1710            signalMastComboBox.setToolTipText(rbx.getString("HintSignalEntry"));
1711            signalHeadComboBox.setToolTipText(rbx.getString("HintSignalEntry"));
1712            panelPercentageSpinner.add(signalPanel);
1713            // On/Off buttons
1714            ButtonGroup onOffGroup = new ButtonGroup();
1715            onOffGroup.add(onButton);
1716            onOffGroup.add(offButton);
1717            panelPercentageSpinner.add(onButton);
1718            panelPercentageSpinner.add(offButton);
1719            panelPercentageSpinner.add(doneSensorLabel);
1720            panelPercentageSpinner.add(doneSensorComboBox);
1721            JComboBoxUtil.setupComboBoxMaxRows(doneSensorComboBox);
1722            doneSensorComboBox.setAllowNull(true);
1723            panelx.add(panelPercentageSpinner);
1724            contentPane.add(panelx);
1725            contentPane.add(new JSeparator());
1726            // add buttons
1727            JPanel but = new JPanel();
1728            but.setLayout(new FlowLayout());
1729            but.add(cancelAddEditActionButton = new JButton(Bundle.getMessage("ButtonCancel")));
1730            cancelAddEditActionButton.addActionListener(this::cancelAddEditActionPressed);
1731            cancelAddEditActionButton.setToolTipText(rbx.getString("CancelButtonHint"));
1732            createActionButton = new JButton(rbx.getString("CreateActionButton"));
1733            but.add(createActionButton);
1734            createActionButton.addActionListener(this::createActionPressed);
1735            createActionButton.setToolTipText(rbx.getString("CreateActionButtonHint"));
1736            updateActionButton = new JButton(rbx.getString("UpdateActionButton"));
1737            but.add(updateActionButton);
1738            updateActionButton.addActionListener(this::updateActionPressed);
1739            updateActionButton.setToolTipText(rbx.getString("UpdateActionButtonHint"));
1740            contentPane.add(but);
1741        }
1742        if (editActionMode) {
1743            // initialize window for the action being edited
1744            addEditActionFrame.setTitle(rbx.getString("TitleEditAction"));
1745            updateActionButton.setVisible(true);
1746            createActionButton.setVisible(false);
1747            whenDataSpinnerInt.setValue(curTSA.getDataWhen());
1748            float whenFloat = (int)whenDataSpinnerInt.getValue();
1749            whenDataSpinnerFloat.setValue(whenFloat/1000.0f);
1750            whenSensorComboBox.setSelectedItemByName(curTSA.getStringWhen());
1751            // spinners are set in setWhat()
1752            tWhatString2 = curTSA.getStringWhat2();
1753            tWhatString = curTSA.getStringWhat();
1754            tWhatData1 = curTSA.getDataWhat1();
1755            tWhat = curTSA.getWhatCode();
1756            tWhen = curTSA.getWhenCode();
1757            tWhenData =  curTSA.getDataWhen();
1758            tWhatData2 = curTSA.getDataWhat2();
1759            whatStringField.setText(tWhatString);
1760            onButton.setSelected(true);
1761            if ("Off".equals(curTSA.getStringWhat())) {
1762                offButton.setSelected(true);
1763            }
1764            locoAddress.setText(curTSA.getStringWhat2());
1765            panelLoadTrainInfo.setVisible(false);
1766            log.debug("setWhen called for edit of action, editmode = {}", editActionMode);
1767            whenBox.setSelectedItem(getWhenMenuText(curTSA.getWhenCode()));
1768            // setWhen(curTSA.getWhenCode()) and setWhat(idem) are set via whenBox and whatBox
1769            whatBox.setSelectedItem(getWhatMenuText(curTSA.getWhatCode()));
1770            setBlockBox();
1771        } else {
1772            // initialize for add new action
1773            addEditActionFrame.setTitle(rbx.getString("TitleAddAction"));
1774            whenBox.setSelectedIndex(0);
1775            // setWhen(1) and setWhat(1) are set from the whenBox and whatBox listeners
1776            whatBox.setSelectedIndex(0);
1777            // set initial values after setting model
1778            whenDataSpinnerInt.setValue(0);
1779            whenDataSpinnerFloat.setValue(0.0f);
1780            whenSensorComboBox.setSelectedItem(0);
1781            whatPercentSpinner.setValue(1.0f);
1782            whatMinuteSpinner1.setValue(100);
1783            whatMinuteSpinner2.setValue(100);
1784            locoFunctionSpinner.setValue(0);
1785            signalMastComboBox.setSelectedItem(0);
1786            signalHeadComboBox.setSelectedItem(0);
1787            doneSensorComboBox.setSelectedItem(0);
1788            whatStringField.setText("");
1789            locoAddress.setText("");
1790            onButton.setSelected(true);
1791            updateActionButton.setVisible(false);
1792            createActionButton.setVisible(true);
1793            panelLoadTrainInfo.setVisible(false);
1794            setBlockBox();
1795        }
1796        addEditActionFrame.addWindowListener(new java.awt.event.WindowAdapter() {
1797            @Override
1798            public void windowClosing(java.awt.event.WindowEvent e) {
1799                    addEditActionFrame.setVisible(false);
1800                    addEditActionFrame.dispose();
1801                    addEditActionFrame = null;
1802            }
1803        });
1804        addEditActionFrame.pack();
1805        addEditActionFrame.setVisible(true);
1806    }
1807
1808    /**
1809     * Set special stuff depending on When selected.
1810     *
1811     * @param code selected item in getWhenBox
1812     */
1813    private void setWhen(int code) {
1814        // setting the whenBox here causes recursion
1815        whenSensorComboBox.setVisible(false);
1816        blockBox.setVisible(false);
1817        panelDelay.setVisible(true);
1818        panelWhatBox.setVisible(true);
1819        panelPercentageSpinner.setVisible(true);
1820        panelDelayLabel.setText("    " + rbx.getString("OptionalDelay") + ": ");
1821        log.debug("setWhen code = {}", code);
1822        initializeWhatBox(code);
1823        switch (code) {
1824            case TransitSectionAction.EXIT:
1825                panelDelay.setVisible(false);
1826                break;
1827            case TransitSectionAction.ENTRY:
1828            case TransitSectionAction.TRAINSTOP:
1829            case TransitSectionAction.TRAINSTART:
1830            case TransitSectionAction.PRESTARTACTION:
1831                break;
1832            case TransitSectionAction.PRESTARTDELAY:
1833                panelDelay.setVisible(true);
1834                panelDelayLabel.setText("    " + rbx.getString("Delay") + ": ");
1835                panelWhatBox.setVisible(false);
1836                panelPercentageSpinner.setVisible(false);
1837                break;
1838            case TransitSectionAction.BLOCKENTRY:
1839            case TransitSectionAction.BLOCKEXIT:
1840                blockBox.setVisible(true);
1841                blockBox.setToolTipText(rbx.getString("HintBlockEntry"));
1842                break;
1843            case TransitSectionAction.SENSORACTIVE:
1844            case TransitSectionAction.SENSORINACTIVE:
1845                whenSensorComboBox.setVisible(true);
1846                whenSensorComboBox.setToolTipText(rbx.getString("HintSensorEntry"));
1847                break;
1848            case TransitSectionAction.SELECTWHEN:
1849                break;
1850            default:
1851                log.debug("Unhandled transit action code: {}", code); // causes too much noise, no harm done hiding it
1852        }
1853        addEditActionFrame.pack();
1854        addEditActionFrame.setVisible(true);
1855    }
1856
1857    /**
1858     * Set special stuff depending on What selected, including spinner value.
1859     *
1860     * @param code selected item in getWhatBox
1861     */
1862    private void setWhat(int code) {
1863        // setting the whatBox here causes recursion
1864        // hide all input boxes, set those needed visible via a switch case
1865        whatStringField.setVisible(false);
1866        whatPercentSpinner.setVisible(false);
1867        whatMinuteSpinner1.setVisible(false);
1868        whatMinuteSpinner2.setVisible(false);
1869        locoFunctionSpinner.setVisible(false);
1870        signalPanel.setVisible(false);
1871        onButton.setVisible(false);
1872        offButton.setVisible(false);
1873        doneSensorLabel.setVisible(false);
1874        doneSensorComboBox.setVisible(false);
1875        panelDelay.setEnabled(true);
1876        panelLoadTrainInfo.setVisible(false);
1877        log.debug("setWhat code = {}", code);
1878        switch (code) {
1879            case TransitSectionAction.TERMINATETRAIN:
1880            case TransitSectionAction.FORCEALLOCATEPASSSAFESECTION:
1881                break;
1882                
1883            case TransitSectionAction.LOADTRAININFO:
1884                rosterComboBox.update();
1885                String[] names = new TrainInfoFile().getTrainInfoFileNames();
1886                trainInfoComboBox.removeAllItems();
1887                for (String fn: names) {
1888                    trainInfoComboBox.addItem(fn);
1889                    if (fn.equals(tWhatString)) {
1890                        trainInfoComboBox.setSelectedItem(fn);
1891                    }
1892                }
1893                locoAddress.setText(Integer.toString(tWhatData1));
1894                switch (tWhatData2) {
1895                    case TransitSectionAction.LOCOADDRESSTYPEROSTER:
1896                        locoAddressRoster.setSelected(true);
1897                        setRosterComboBox(rosterComboBox, tWhatString2);
1898                        rosterComboBox.setVisible(true);
1899                        locoAddress.setVisible(false);
1900                        break;
1901                    case TransitSectionAction.LOCOADDRESSTYPENUMBER:
1902                        locoAddressNumber.setSelected(true);
1903                        locoAddress.setText(tWhatString2);
1904                        rosterComboBox.setVisible(false);
1905                        locoAddress.setVisible(true);
1906                        break;
1907                    case TransitSectionAction.LOCOADDRESSTYPECURRENT:
1908                        locoAddressCurrent.setSelected(true);
1909                        locoAddress.setText("");
1910                        rosterComboBox.setVisible(false);
1911                        locoAddress.setVisible(false);
1912                        break;
1913                    case TransitSectionAction.LOCOADDRESSTYPEDEFAULT:
1914                    default:
1915                        locoAddressDefault.setSelected(true);
1916                        rosterComboBox.setVisible(false);
1917                        locoAddress.setVisible(false);
1918                        locoAddress.setText("");
1919                        break;
1920                }
1921                panelLoadTrainInfo.setVisible(true);
1922                break;
1923            case TransitSectionAction.PAUSE:
1924                if (getWhenMenuCode((String)whenBox.getSelectedItem()) == TransitSectionAction.PRESTARTDELAY) {
1925                    panelDelay.setEnabled(false);
1926                }
1927                whatMinuteSpinner1.setModel(new SpinnerNumberModel(1, 1, 65500, 1));
1928                if (editActionMode) {
1929                    whatMinuteSpinner1.setValue(Math.max(curTSA.getDataWhat1(), 1));
1930                }
1931                whatMinuteSpinner1.setVisible(true);
1932                whatMinuteSpinner1.setToolTipText(rbx.getString("HintPauseData"));
1933                break;
1934            case TransitSectionAction.SETMAXSPEED:
1935            case TransitSectionAction.SETCURRENTSPEED:
1936            case TransitSectionAction.RAMPTRAINSPEED:
1937                if (editActionMode) {
1938                    float maxPerc = Math.max(0.01f * curTSA.getDataWhat1(), 0.0f);
1939                    whatPercentSpinner.setValue(maxPerc);
1940                }
1941                whatPercentSpinner.setVisible(true);
1942                whatPercentSpinner.setToolTipText(rbx.getString("HintSetSpeedData1"));
1943                break;
1944            case TransitSectionAction.TOMANUALMODE:
1945                if (editActionMode) {
1946                    doneSensorComboBox.setSelectedItemByName(curTSA.getStringWhat());
1947                }
1948                doneSensorLabel.setVisible(true);
1949                doneSensorComboBox.setVisible(true);
1950                doneSensorComboBox.setToolTipText(rbx.getString("HintDoneSensor"));
1951                break;
1952            case TransitSectionAction.SETLIGHT:
1953                onButton.setVisible(true);
1954                offButton.setVisible(true);
1955                onButton.setToolTipText(rbx.getString("HintSetLight"));
1956                offButton.setToolTipText(rbx.getString("HintSetLight"));
1957                break;
1958            case TransitSectionAction.STARTBELL:
1959                break;
1960            case TransitSectionAction.STOPBELL:
1961                break;
1962            case TransitSectionAction.SOUNDHORN:
1963                whatMinuteSpinner1.setValue(100);
1964                whatMinuteSpinner1.setModel(new SpinnerNumberModel(100, 100, 65500, 1));
1965                if (editActionMode) {
1966                    whatMinuteSpinner1.setValue(curTSA.getDataWhat1());
1967                }
1968                if ((Integer) whatMinuteSpinner1.getValue() < 100) {
1969                    whatMinuteSpinner1.setValue(100); // might result from user changing from PAUSE to SOUNDHORN
1970                }
1971                whatMinuteSpinner1.setVisible(true);
1972                whatMinuteSpinner1.setToolTipText(rbx.getString("HintSoundHornData1"));
1973                break;
1974            case TransitSectionAction.SOUNDHORNPATTERN:
1975                whatMinuteSpinner1.setValue(100);
1976                whatMinuteSpinner1.setModel(new SpinnerNumberModel(100, 100, 65500, 1));
1977                // whatMinuteSpinner2 model never changes
1978                if (editActionMode) {
1979                    whatMinuteSpinner1.setValue(curTSA.getDataWhat1());
1980                    whatMinuteSpinner2.setValue(Math.max(curTSA.getDataWhat2(), 100));
1981                    // might result from user changing from sth.else to SOUNDHORNPATTERN
1982                }
1983                if ((Integer) whatMinuteSpinner1.getValue() < 100) {
1984                    whatMinuteSpinner1.setValue(100); // might result from user changing from PAUSE to SOUNDHORNPATTERN
1985                }
1986                whatMinuteSpinner1.setVisible(true);
1987                whatMinuteSpinner1.setToolTipText(rbx.getString("HintSoundHornPatternData1"));
1988                whatMinuteSpinner2.setVisible(true);
1989                whatMinuteSpinner2.setToolTipText(rbx.getString("HintSoundHornPatternData2"));
1990                whatStringField.setVisible(true);
1991                break;
1992            case TransitSectionAction.LOCOFUNCTION:
1993                if (editActionMode) {
1994                    locoFunctionSpinner.setValue(curTSA.getDataWhat1());
1995                }
1996                locoFunctionSpinner.setVisible(true);
1997                locoFunctionSpinner.setToolTipText(rbx.getString("HintLocoFunctionData1"));
1998                onButton.setVisible(true);
1999                offButton.setVisible(true);
2000                onButton.setToolTipText(rbx.getString("HintLocoFunctionOnOff"));
2001                offButton.setToolTipText(rbx.getString("HintLocoFunctionOnOff"));
2002                break;
2003            case TransitSectionAction.SETSENSORACTIVE:
2004            case TransitSectionAction.SETSENSORINACTIVE:
2005                if (editActionMode) {
2006                    doneSensorComboBox.setSelectedItemByName(curTSA.getStringWhat());
2007                }
2008                doneSensorComboBox.setVisible(true);
2009                doneSensorComboBox.setToolTipText(rbx.getString("HintSensorEntry"));
2010                break;
2011            case TransitSectionAction.HOLDSIGNAL:
2012            case TransitSectionAction.RELEASESIGNAL:
2013                if (editActionMode) {
2014                    SignalMast sm = InstanceManager.getDefault(SignalMastManager.class).getSignalMast(curTSA.getStringWhat());
2015                    if (sm != null) { // name is an existing mast
2016                        signalMastComboBox.setSelectedItemByName(curTSA.getStringWhat());
2017                    } else {
2018                        SignalHead sh = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(curTSA.getStringWhat());
2019                        if (sh != null) { // name is an existing head
2020                            signalHeadComboBox.setSelectedItemByName(curTSA.getStringWhat());
2021                        }
2022                    }
2023                }
2024                signalPanel.setVisible(true);
2025                break;
2026            case TransitSectionAction.ESTOP:
2027            default:
2028                log.debug("Unhandled transit section action: {}", code); // causes too much noise, no harm done hiding it
2029                break;
2030        }
2031        addEditActionFrame.pack();
2032        addEditActionFrame.setVisible(true);
2033    }
2034
2035    private boolean setRosterComboBox(RosterEntryComboBox box, String txt) {
2036        boolean found = false;
2037        for (int i = 1; i < box.getItemCount(); i++) {
2038            if (txt.equals(((RosterEntry) box.getItemAt(i)).getId())) {
2039                box.setSelectedIndex(i);
2040                found = true;
2041                break;
2042            }
2043        }
2044        if (!found && box.getItemCount() > 0) {
2045            box.setSelectedIndex(0);
2046        }
2047        return found;
2048    }
2049
2050    private void updateTrainInfoAddressFields(ActionEvent e) {
2051        if (!((JRadioButton)e.getSource()).isSelected() ) {
2052            return;
2053        }
2054        if (e.getSource() == locoAddressRoster) {
2055            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEROSTER;
2056            rosterComboBox.setVisible(true);
2057            locoAddress.setVisible(false);
2058        } else if (e.getSource() == locoAddressNumber) {
2059            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPENUMBER;
2060            rosterComboBox.setVisible(false);
2061            locoAddress.setVisible(true);
2062        } else if (e.getSource() == locoAddressDefault) {
2063            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEDEFAULT;
2064            rosterComboBox.setVisible(false);
2065            locoAddress.setVisible(false);
2066        } else if (e.getSource() == locoAddressCurrent) {
2067            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPECURRENT;
2068            rosterComboBox.setVisible(false);
2069            locoAddress.setVisible(false);
2070        } else {
2071            log.warn("Unknown button Source");
2072        }
2073    }
2074
2075    // temporary action variables
2076    private int tWhen = 0;
2077    private int tWhenData = 0;
2078    private String tWhenString = "";
2079    private int tWhat = 0;
2080    private int tWhatData1 = 0;
2081    private int tWhatData2 = 0;
2082    private String tWhatString = "";
2083    private String tWhatString2 = "";
2084
2085    /**
2086     * Handle button presses in Add/Edit Transit Action window.
2087     *
2088     * @param e the event seen
2089     */
2090    private void createActionPressed(ActionEvent e) {
2091        if ((!validateWhenData()) || (!validateWhatData())) {
2092            return;
2093        }
2094        // entered data is OK, create a special action
2095        curTSA = new TransitSectionAction(tWhen, tWhat, tWhenData, tWhatData1, tWhatData2, tWhenString, tWhatString, tWhatString2);
2096        List<TransitSectionAction> list = action.get(activeRow);
2097        list.add(curTSA);
2098        actionTableModel.fireTableDataChanged();
2099        addEditActionFrame.setVisible(false);
2100        addEditActionFrame.dispose();
2101        addEditActionFrame = null;
2102    }
2103
2104    private void updateActionPressed(ActionEvent e) {
2105        if ((!validateWhenData()) || (!validateWhatData())) {
2106            return;
2107        }
2108        // entered data is OK, update the current special action
2109        curTSA.setWhenCode(tWhen);
2110        curTSA.setWhatCode(tWhat);
2111        curTSA.setDataWhen(tWhenData);
2112        curTSA.setDataWhat1(tWhatData1);
2113        curTSA.setDataWhat2(tWhatData2);
2114        curTSA.setStringWhen(tWhenString);
2115        curTSA.setStringWhat(tWhatString);
2116        curTSA.setStringWhat2(tWhatString2);
2117        actionTableModel.fireTableDataChanged();
2118        addEditActionFrame.setVisible(false);
2119        addEditActionFrame.dispose();
2120        addEditActionFrame = null;
2121    }
2122
2123    private void cancelAddEditActionPressed(ActionEvent e) {
2124        addEditActionFrame.setVisible(false);
2125        addEditActionFrame.dispose();
2126        addEditActionFrame = null;
2127    }
2128
2129    private boolean validateWhenData() {
2130        tWhen = getWhenMenuCode((String)whenBox.getSelectedItem());
2131        tWhenData = (int)whenDataSpinnerInt.getValue();
2132        tWhenString = "";
2133        if (tWhen == TransitSectionAction.PRESTARTDELAY ) {
2134            // must have a delay
2135            if (tWhenData <1 ) {
2136                return false;
2137            }
2138        }
2139        if ((tWhen == TransitSectionAction.SENSORACTIVE) || (tWhen == TransitSectionAction.SENSORINACTIVE)) {
2140            if (whenSensorComboBox.getSelectedIndex() != 0) { // it's optional, so might be 0
2141                tWhenString = whenSensorComboBox.getSelectedItemSystemName();
2142            }
2143            if (!validateSensor(tWhenString, true)) {
2144                return false;
2145            }
2146        }
2147        if ((tWhen == TransitSectionAction.BLOCKENTRY) || (tWhen == TransitSectionAction.BLOCKEXIT)) {
2148            tWhenString = blockList.get(blockBox.getSelectedIndex()).getSystemName();
2149        }
2150        return true;
2151    }
2152
2153    private boolean validateSensor(String sName, boolean when) {
2154        // check if anything entered
2155        if (sName.length() < 1) {
2156            // no sensor selected
2157            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("NoSensorError")),
2158                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2159            return false;
2160        }
2161        // get the sensor corresponding to this name
2162        Sensor s = InstanceManager.sensorManagerInstance().getSensor(sName);
2163        if (s == null) {
2164            // There is no sensor corresponding to this name
2165            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("SensorEntryError")),
2166                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2167            return false;
2168        }
2169        if (!sName.equals(s.getUserName())) {
2170            if (when) {
2171                tWhenString = sName;
2172            } else {
2173                tWhatString = sName;
2174            }
2175        }
2176        return true;
2177    }
2178
2179    private boolean validateSignal(String sName, boolean when) {
2180        // check if anything is selected
2181        if (sName.length() < 1) {
2182            // no signal selected
2183            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("NoSignalError")),
2184                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2185            return false;
2186        }
2187        // get the signalMast or signalHead corresponding to this name
2188        SignalMast sm = InstanceManager.getDefault(SignalMastManager.class).getSignalMast(sName);
2189        SignalHead sh = null;
2190        if (sm == null) {
2191            sh = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(sName);
2192        }
2193        if (sm == null && sh == null) {
2194            // There is no signal corresponding to this name
2195            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("SignalEntryError")),
2196                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2197            return false;
2198        }
2199        return true;
2200    }
2201
2202    /**
2203     * Validate entered data for selected Action. Converted to use JSpinners
2204     * where applicable, 2017.
2205     *
2206     * @return true if data entered into field whatStringField is valid for selected Action type tWhat
2207     */
2208    private boolean validateWhatData() {
2209        tWhat = getWhatMenuCode((String)whatBox.getSelectedItem());
2210        tWhatData1 = 0;
2211        tWhatData2 = 0;
2212        tWhatString = "";
2213        switch (tWhat) {
2214            case TransitSectionAction.SETMAXSPEED:
2215            case TransitSectionAction.SETCURRENTSPEED:
2216            case TransitSectionAction.RAMPTRAINSPEED:
2217                tWhatData1 = Math.round(100 * (float) whatPercentSpinner.getValue());
2218                break;
2219            case TransitSectionAction.TOMANUALMODE:
2220                tWhatString="";
2221                if (doneSensorComboBox.getSelectedIndex() >= 0) { // it's optional, so might be -1
2222                    tWhatString = doneSensorComboBox.getSelectedItemSystemName(); // sensor system name
2223                }
2224                if (tWhatString.length() >= 1) {
2225                    if (!validateSensor(tWhatString, false)) {
2226                        tWhatString = "";
2227                    }
2228                }
2229                break;
2230            case TransitSectionAction.SETLIGHT:
2231                tWhatString = "On"; // NOI18N
2232                if (offButton.isSelected()) {
2233                    tWhatString = "Off"; // NOI18N
2234                }
2235                break;
2236            case TransitSectionAction.STARTBELL:
2237            case TransitSectionAction.STOPBELL:
2238            case TransitSectionAction.TERMINATETRAIN:
2239            case TransitSectionAction.FORCEALLOCATEPASSSAFESECTION:
2240                break;
2241            case TransitSectionAction.LOADTRAININFO:
2242                if (trainInfoComboBox.getSelectedIndex() < 0 ) {
2243                    JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingTrainInfoFile")),
2244                            Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2245                    return false;
2246                }
2247                tWhatString = (String)trainInfoComboBox.getSelectedItem();
2248                if (locoAddressRoster.isSelected()) {
2249                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEROSTER;
2250                    // the first item is "select..."
2251                    if (rosterComboBox.getSelectedIndex() < 1) {
2252                        JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingRosterEntryOrLocoAddress")),
2253                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2254                        return false;
2255                    }
2256                    tWhatString2 =((RosterEntry) rosterComboBox.getSelectedItem()).getId();
2257                } else if (locoAddressNumber.isSelected()) {
2258                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPENUMBER;
2259                    tWhatString2 = locoAddress.getText();
2260                    if ((tWhatString2 == null) || tWhatString2.isEmpty() || (tWhatString2.length() < 1)) {
2261                        JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingRosterEntryOrLocoAddress")),
2262                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2263                        return false;
2264                    }
2265                } else if (locoAddressDefault.isSelected()) {
2266                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEDEFAULT;
2267                    tWhatString2 = "";
2268                } else if (locoAddressCurrent.isSelected()) {
2269                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPECURRENT;
2270                    tWhatString2 = "";
2271                } else {
2272                    JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("UnKnownlocoaddresstype")),
2273                            Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2274                    return false;
2275                }
2276                break;
2277            case TransitSectionAction.PAUSE:
2278            case TransitSectionAction.SOUNDHORN:
2279                tWhatData1 = (Integer) whatMinuteSpinner1.getValue();
2280                break;
2281            case TransitSectionAction.SOUNDHORNPATTERN:
2282                tWhatData1 = (Integer) whatMinuteSpinner1.getValue();
2283                tWhatData2 = (Integer) whatMinuteSpinner2.getValue();
2284                tWhatString = whatStringField.getText();
2285                if ((tWhatString == null) || tWhatString.isEmpty() || (tWhatString.length() < 1)) {
2286                    JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingPattern")),
2287                            Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2288                    return false;
2289                }
2290                tWhatString = tWhatString.trim().toLowerCase();
2291                for (int i = 0; i < tWhatString.length(); i++) {
2292                    char c = tWhatString.charAt(i);
2293                    if ((c != 's') && (c != 'l')) {
2294                        JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("ErrorPattern")),
2295                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2296                        return false;
2297                    }
2298                }
2299                whatStringField.setText(tWhatString); // re-enter normalized value in display field
2300                break;
2301            case TransitSectionAction.LOCOFUNCTION:
2302                tWhatData1 = (Integer) locoFunctionSpinner.getValue();
2303                tWhatString = "On"; // NOI18N
2304                if (offButton.isSelected()) {
2305                    tWhatString = "Off"; // NOI18N
2306                }
2307                break;
2308            case TransitSectionAction.SETSENSORACTIVE:
2309            case TransitSectionAction.SETSENSORINACTIVE:
2310                if (doneSensorComboBox.getSelectedIndex() > 0) {
2311                    tWhatString = doneSensorComboBox.getSelectedItemSystemName();
2312                }
2313                if (!validateSensor(tWhatString, false)) {
2314                    return false;
2315                }
2316                break;
2317            case TransitSectionAction.HOLDSIGNAL:
2318            case TransitSectionAction.RELEASESIGNAL:
2319                if (signalMastComboBox.getSelectedIndex() > 0) {
2320                    tWhatString = signalMastComboBox.getSelectedItemSystemName();
2321                } else if (signalHeadComboBox.getSelectedIndex() > 0) {
2322                    tWhatString = signalHeadComboBox.getSelectedItemSystemName();
2323                }
2324                if (!validateSignal(tWhatString, false)) {
2325                    return false;
2326                }
2327                break;
2328            case TransitSectionAction.PRESTARTRESUME:
2329                break;
2330            default:
2331                log.warn("Unhandled transit section action code: {}", tWhat);
2332                break;
2333        }
2334        return true;
2335    }
2336
2337    // initialize combos for add/edit action window
2338    private void initializeWhenBox() {
2339        whenBox.removeAllItems();
2340        for (int i = 0; i <= TransitSectionAction.NUM_WHENS; i++) {
2341            whenBox.addItem(getWhenMenuText(i));
2342        }
2343    }
2344
2345    private String getWhenMenuText(int i) {
2346        switch (i) {
2347            case TransitSectionAction.ENTRY:
2348                return rbx.getString("OnEntry");
2349            case TransitSectionAction.EXIT:
2350                return rbx.getString("OnExit");
2351            case TransitSectionAction.BLOCKENTRY:
2352                return rbx.getString("OnBlockEntry");
2353            case TransitSectionAction.BLOCKEXIT:
2354                return rbx.getString("OnBlockExit");
2355            case TransitSectionAction.TRAINSTOP:
2356                return rbx.getString("TrainStop");
2357            case TransitSectionAction.TRAINSTART:
2358                return rbx.getString("TrainStart");
2359            case TransitSectionAction.SENSORACTIVE:
2360                return rbx.getString("OnSensorActive");
2361            case TransitSectionAction.SENSORINACTIVE:
2362                return rbx.getString("OnSensorInactive");
2363            case TransitSectionAction.PRESTARTDELAY:
2364                return rbx.getString("PreStartDelay");
2365            case TransitSectionAction.PRESTARTACTION:
2366                return rbx.getString("PreStartAction");
2367            case TransitSectionAction.SELECTWHEN:
2368                return rbx.getString("SelectWhen");
2369            default:
2370                log.warn("Unhandled transit section when code: {}", i);
2371                return rbx.getString("SelectWhen");
2372        }
2373    }
2374
2375    private int getWhenMenuCode(String s) {
2376        if (s.equals(rbx.getString("OnEntry"))) {
2377            return TransitSectionAction.ENTRY;
2378        }
2379        if (s.equals(rbx.getString("OnExit"))) {
2380            return TransitSectionAction.EXIT;
2381        }
2382        if (s.equals(rbx.getString("OnBlockEntry"))) {
2383            return TransitSectionAction.BLOCKENTRY;
2384        }
2385        if (s.equals(rbx.getString("OnBlockExit"))) {
2386            return TransitSectionAction.BLOCKEXIT;
2387        }
2388        if (s.equals(rbx.getString("TrainStop"))) {
2389            return TransitSectionAction.TRAINSTOP;
2390        }
2391        if (s.equals(rbx.getString("TrainStart"))) {
2392            return TransitSectionAction.TRAINSTART;
2393        }
2394        if (s.equals(rbx.getString("OnSensorActive"))) {
2395            return TransitSectionAction.SENSORACTIVE;
2396        }
2397        if (s.equals(rbx.getString("OnSensorInactive"))) {
2398            return TransitSectionAction.SENSORINACTIVE;
2399        }
2400        if (s.equals(rbx.getString("PreStartDelay"))) {
2401            return TransitSectionAction.PRESTARTDELAY;
2402        }
2403        if (s.equals(rbx.getString("PreStartAction"))) {
2404            return TransitSectionAction.PRESTARTACTION;
2405        }
2406        return TransitSectionAction.SELECTWHEN;
2407    }
2408
2409    private void initializeWhatBox(int code) {
2410        whatBox.removeAllItems();
2411        List<Integer> excludeCodes = new ArrayList<>();
2412        List<Integer> includeCodes = new ArrayList<>();
2413        if (code == TransitSectionAction.PRESTARTACTION) {
2414            // exclude speed changing as that messes up the prestart.List<Section> sectionList = new ArrayList<>();
2415            excludeCodes = new ArrayList<>(Arrays.asList(TransitSectionAction.SETMAXSPEED, TransitSectionAction.SETCURRENTSPEED,
2416                            TransitSectionAction.RAMPTRAINSPEED));
2417        }    else if (code == TransitSectionAction.PRESTARTDELAY) {
2418            includeCodes.add(TransitSectionAction.PRESTARTRESUME);
2419        } else {
2420            excludeCodes.add(TransitSectionAction.PRESTARTRESUME);
2421        }
2422        for (int i = 0; i <= TransitSectionAction.NUM_WHATS; i++) {
2423            if (!excludeCodes.isEmpty()) {
2424                if (! excludeCodes.contains(i)) {
2425                    whatBox.addItem(getWhatMenuText(i));
2426                }
2427            } else if (!includeCodes.isEmpty()) {
2428                if (includeCodes.contains(i)) {
2429                    whatBox.addItem(getWhatMenuText(i));
2430                }
2431            } else {
2432                whatBox.addItem(getWhatMenuText(i));
2433            }
2434        }
2435    }
2436
2437    @Nonnull
2438    private String getWhatMenuText(int i) {
2439        switch (i) {
2440            case TransitSectionAction.SELECTWHAT:
2441                return rbx.getString("SelectWhat");
2442            case TransitSectionAction.TERMINATETRAIN:
2443                return rbx.getString("TerminateTrain");
2444            case TransitSectionAction.LOADTRAININFO:
2445                return rbx.getString("LoadTrainInfo");
2446            case TransitSectionAction.FORCEALLOCATEPASSSAFESECTION:
2447                return rbx.getString("ForcePassNextSafe");
2448            case TransitSectionAction.PAUSE:
2449                return rbx.getString("Pause");
2450            case TransitSectionAction.SETMAXSPEED:
2451                return rbx.getString("SetMaxSpeed");
2452            case TransitSectionAction.SETCURRENTSPEED:
2453                return rbx.getString("SetTrainSpeed");
2454            case TransitSectionAction.RAMPTRAINSPEED:
2455                return rbx.getString("RampTrainSpeed");
2456            case TransitSectionAction.TOMANUALMODE:
2457                return rbx.getString("ToManualMode");
2458            case TransitSectionAction.SETLIGHT:
2459                return rbx.getString("SetLight");
2460            case TransitSectionAction.STARTBELL:
2461                return rbx.getString("StartBell");
2462            case TransitSectionAction.STOPBELL:
2463                return rbx.getString("StopBell");
2464            case TransitSectionAction.SOUNDHORN:
2465                return rbx.getString("SoundHorn");
2466            case TransitSectionAction.SOUNDHORNPATTERN:
2467                return rbx.getString("SoundHornPattern");
2468            case TransitSectionAction.LOCOFUNCTION:
2469                return rbx.getString("LocoFunction");
2470            case TransitSectionAction.SETSENSORACTIVE:
2471                return rbx.getString("SetSensorActive");
2472            case TransitSectionAction.SETSENSORINACTIVE:
2473                return rbx.getString("SetSensorInactive");
2474            case TransitSectionAction.HOLDSIGNAL:
2475                return rbx.getString("HoldSignal");
2476            case TransitSectionAction.RELEASESIGNAL:
2477                return rbx.getString("ReleaseSignal");
2478            case TransitSectionAction.ESTOP:
2479                return rbx.getString("EStop");
2480            case TransitSectionAction.PRESTARTRESUME:
2481                return rbx.getString("PreStartResume");
2482            default:
2483                log.warn("Unhandled transit section action code: {}", i);
2484                return rbx.getString("SelectWhat");
2485        }
2486    }
2487
2488    private int getWhatMenuCode(@Nonnull String s) {
2489        if (s.equals(rbx.getString("SelectWhat"))) {
2490            return TransitSectionAction.SELECTWHAT;
2491        }
2492        if (s.equals(rbx.getString("TerminateTrain"))) {
2493            return TransitSectionAction.TERMINATETRAIN;
2494        }
2495        if (s.equals(rbx.getString("ForcePassNextSafe"))) {
2496            return TransitSectionAction.FORCEALLOCATEPASSSAFESECTION;
2497        }
2498        if (s.equals(rbx.getString("LoadTrainInfo"))) {
2499            return TransitSectionAction.LOADTRAININFO;
2500        }
2501        if (s.equals(rbx.getString("Pause"))) {
2502            return TransitSectionAction.PAUSE;
2503        }
2504        if (s.equals(rbx.getString("SetMaxSpeed"))) {
2505            return TransitSectionAction.SETMAXSPEED;
2506        }
2507        if (s.equals(rbx.getString("SetTrainSpeed"))) {
2508            return TransitSectionAction.SETCURRENTSPEED;
2509        }
2510        if (s.equals(rbx.getString("RampTrainSpeed"))) {
2511            return TransitSectionAction.RAMPTRAINSPEED;
2512        }
2513        if (s.equals(rbx.getString("ToManualMode"))) {
2514            return TransitSectionAction.TOMANUALMODE;
2515        }
2516        if (s.equals(rbx.getString("SetLight"))) {
2517            return TransitSectionAction.SETLIGHT;
2518        }
2519        if (s.equals(rbx.getString("StartBell"))) {
2520            return TransitSectionAction.STARTBELL;
2521        }
2522        if (s.equals(rbx.getString("StopBell"))) {
2523            return TransitSectionAction.STOPBELL;
2524        }
2525        if (s.equals(rbx.getString("SoundHorn"))) {
2526            return TransitSectionAction.SOUNDHORN;
2527        }
2528        if (s.equals(rbx.getString("SoundHornPattern"))) {
2529            return TransitSectionAction.SOUNDHORNPATTERN;
2530        }
2531        if (s.equals(rbx.getString("LocoFunction"))) {
2532            return TransitSectionAction.LOCOFUNCTION;
2533        }
2534        if (s.equals(rbx.getString("SetSensorActive"))) {
2535            return TransitSectionAction.SETSENSORACTIVE;
2536        }
2537        if (s.equals(rbx.getString("SetSensorInactive"))) {
2538            return TransitSectionAction.SETSENSORINACTIVE;
2539        }
2540        if (s.equals(rbx.getString("HoldSignal"))) {
2541            return TransitSectionAction.HOLDSIGNAL;
2542        }
2543        if (s.equals(rbx.getString("ReleaseSignal"))) {
2544            return TransitSectionAction.RELEASESIGNAL;
2545        }
2546        if (s.equals(rbx.getString("EStop"))) {
2547            return TransitSectionAction.ESTOP;
2548        }
2549        if (s.equals(rbx.getString("PreStartResume"))) {
2550            return TransitSectionAction.PRESTARTRESUME;
2551        }
2552        log.warn("Unhandled transit section action text: {}", s);
2553        return 0;
2554    }
2555
2556    private void initializeBlockBox() {
2557        blockList = sectionList.get(activeRow).getBlockList();
2558        blockBox.removeAllItems();
2559        for (int i = 0; i < blockList.size(); i++) {
2560            String s = blockList.get(i).getDisplayName();
2561            blockBox.addItem(s);
2562        }
2563    }
2564
2565    private void setBlockBox() {
2566        if (editActionMode) {
2567            if ((curTSA.getWhenCode() == TransitSectionAction.BLOCKENTRY)
2568                    || (curTSA.getWhenCode() == TransitSectionAction.BLOCKEXIT)) {
2569                // assumes that initializeBlockBox has been called prior to this call
2570                for (int i = 0; i < blockList.size(); i++) {
2571                    if (curTSA.getStringWhen().equals(blockList.get(i).getSystemName())) {
2572                        blockBox.setSelectedIndex(i);
2573                        return;
2574                    }
2575                }
2576            }
2577        }
2578        blockBox.setSelectedIndex(0);
2579    }
2580
2581    private void editAction(int r) {
2582        curTSA = action.get(activeRow).get(r);
2583        editActionMode = true;
2584        addEditActionWindow();
2585    }
2586
2587    private void deleteAction(int r) {
2588        TransitSectionAction tsa = action.get(activeRow).get(r);
2589        action.get(activeRow).remove(r);
2590        tsa.dispose();
2591        actionTableModel.fireTableDataChanged();
2592    }
2593
2594    /**
2595     * Build display When string for Actions table.
2596     *
2597     * @param r row in the Special Actions table. A TransitSectionAction must be
2598     *          available for this row.
2599     * @return display string including entered values
2600     */
2601    private String getWhenText(int r) {
2602        TransitSectionAction tsa = action.get(activeRow).get(r);
2603        switch (tsa.getWhenCode()) {
2604            case TransitSectionAction.ENTRY:
2605                if (tsa.getDataWhen() > 0) {
2606                    return java.text.MessageFormat.format(rbx.getString("OnEntryDelayedFull"),
2607                            new Object[]{"" + tsa.getDataWhen()});
2608                }
2609                return rbx.getString("OnEntryFull");
2610            case TransitSectionAction.EXIT:
2611                if (tsa.getDataWhen() > 0) {
2612                    return java.text.MessageFormat.format(rbx.getString("OnExitDelayedFull"),
2613                            new Object[]{"" + tsa.getDataWhen()});
2614                }
2615                return rbx.getString("OnExitFull");
2616            case TransitSectionAction.BLOCKENTRY:
2617                if (tsa.getDataWhen() > 0) {
2618                    return java.text.MessageFormat.format(rbx.getString("OnBlockEntryDelayedFull"),
2619                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2620                }
2621                return java.text.MessageFormat.format(rbx.getString("OnBlockEntryFull"),
2622                        new Object[]{tsa.getStringWhen()});
2623            case TransitSectionAction.BLOCKEXIT:
2624                if (tsa.getDataWhen() > 0) {
2625                    return java.text.MessageFormat.format(rbx.getString("OnBlockExitDelayedFull"),
2626                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2627                }
2628                return java.text.MessageFormat.format(rbx.getString("OnBlockExitFull"),
2629                        new Object[]{tsa.getStringWhen()});
2630            case TransitSectionAction.TRAINSTOP:
2631                if (tsa.getDataWhen() > 0) {
2632                    return java.text.MessageFormat.format(rbx.getString("TrainStopDelayedFull"),
2633                            new Object[]{"" + tsa.getDataWhen()});
2634                }
2635                return rbx.getString("TrainStopFull");
2636            case TransitSectionAction.TRAINSTART:
2637                if (tsa.getDataWhen() > 0) {
2638                    return java.text.MessageFormat.format(rbx.getString("TrainStartDelayedFull"),
2639                            new Object[]{"" + tsa.getDataWhen()});
2640                }
2641                return rbx.getString("TrainStartFull");
2642            case TransitSectionAction.SENSORACTIVE:
2643                if (tsa.getDataWhen() > 0) {
2644                    return java.text.MessageFormat.format(rbx.getString("OnSensorActiveDelayedFull"),
2645                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2646                }
2647                return java.text.MessageFormat.format(rbx.getString("OnSensorActiveFull"),
2648                        new Object[]{tsa.getStringWhen()});
2649            case TransitSectionAction.SENSORINACTIVE:
2650                if (tsa.getDataWhen() > 0) {
2651                    return java.text.MessageFormat.format(rbx.getString("OnSensorInactiveDelayedFull"),
2652                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2653                }
2654                return java.text.MessageFormat.format(rbx.getString("OnSensorInactiveFull"),
2655                        new Object[]{tsa.getStringWhen()});
2656            case TransitSectionAction.PRESTARTDELAY:
2657                return java.text.MessageFormat.format(rbx.getString("PreStartDelayFull"),
2658                        new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2659            case TransitSectionAction.PRESTARTACTION:
2660                return java.text.MessageFormat.format(rbx.getString("PreStartActionFull"),
2661                        new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2662            default:
2663                log.warn("Unhandled transit section action When code: {}",tsa.getWhenCode());
2664                return("");
2665        }
2666    }
2667
2668    /**
2669     * Build display What string for Actions table.
2670     *
2671     * @param r row in the Special Actions table. A TransitSectionAction must be
2672     *          available for this row.
2673     * @return display string including entered values
2674     */
2675    private String getWhatText(int r) {
2676        TransitSectionAction tsa = action.get(activeRow).get(r);
2677        switch (tsa.getWhatCode()) {
2678            case TransitSectionAction.PAUSE:
2679                return java.text.MessageFormat.format(rbx.getString("PauseFull"),
2680                        new Object[]{tsa.getDataWhat1()});
2681            case TransitSectionAction.SETMAXSPEED:
2682                return java.text.MessageFormat.format(rbx.getString("SetMaxSpeedFull"),
2683                        new Object[]{tsa.getDataWhat1()});
2684            case TransitSectionAction.SETCURRENTSPEED:
2685                return java.text.MessageFormat.format(rbx.getString("SetTrainSpeedFull"),
2686                        new Object[]{tsa.getDataWhat1()});
2687            case TransitSectionAction.RAMPTRAINSPEED:
2688                return java.text.MessageFormat.format(rbx.getString("RampTrainSpeedFull"),
2689                        new Object[]{"" + tsa.getDataWhat1()});
2690            case TransitSectionAction.TOMANUALMODE:
2691                if (tsa.getStringWhat().length() > 0) {
2692                    return java.text.MessageFormat.format(rbx.getString("ToManualModeAltFull"),
2693                            new Object[]{tsa.getStringWhat()});
2694                }
2695                return rbx.getString("ToManualModeFull");
2696            case TransitSectionAction.SETLIGHT:
2697                if (tsa.getStringWhat().equals("Off")) {
2698                    return java.text.MessageFormat.format(rbx.getString("SetLightFull"),
2699                        new Object[]{Bundle.getMessage("StateOff")});
2700                }
2701                return java.text.MessageFormat.format(rbx.getString("SetLightFull"),
2702                        new Object[]{Bundle.getMessage("StateOn")});
2703            case TransitSectionAction.STARTBELL:
2704                return rbx.getString("StartBellFull");
2705            case TransitSectionAction.STOPBELL:
2706                return rbx.getString("StopBellFull");
2707            case TransitSectionAction.SOUNDHORN:
2708                return java.text.MessageFormat.format(rbx.getString("SoundHornFull"),
2709                        new Object[]{tsa.getDataWhat1()});
2710            case TransitSectionAction.SOUNDHORNPATTERN:
2711                return java.text.MessageFormat.format(rbx.getString("SoundHornPatternFull"),
2712                        new Object[]{tsa.getStringWhat(), "" + tsa.getDataWhat1(), "" + tsa.getDataWhat2()});
2713            case TransitSectionAction.LOCOFUNCTION:
2714                if (tsa.getStringWhat().equals("Off")) {
2715                    return java.text.MessageFormat.format(rbx.getString("LocoFunctionFull"),
2716                            new Object[]{"" + tsa.getDataWhat1(), Bundle.getMessage("StateOff")});
2717                }
2718                return java.text.MessageFormat.format(rbx.getString("LocoFunctionFull"),
2719                        new Object[]{"" + tsa.getDataWhat1(), Bundle.getMessage("StateOn")});
2720            case TransitSectionAction.SETSENSORACTIVE:
2721                return java.text.MessageFormat.format(rbx.getString("SetSensorActiveFull"),
2722                        new Object[]{tsa.getStringWhat()});
2723            case TransitSectionAction.SETSENSORINACTIVE:
2724                return java.text.MessageFormat.format(rbx.getString("SetSensorInactiveFull"),
2725                        new Object[]{tsa.getStringWhat()});
2726            case TransitSectionAction.HOLDSIGNAL:
2727                return java.text.MessageFormat.format(rbx.getString("HoldSignalFull"),
2728                        new Object[]{tsa.getStringWhat()});
2729            case TransitSectionAction.RELEASESIGNAL:
2730                return java.text.MessageFormat.format(rbx.getString("ReleaseSignalFull"),
2731                        new Object[]{tsa.getStringWhat()});
2732            case TransitSectionAction.PRESTARTRESUME:
2733                return java.text.MessageFormat.format(rbx.getString("PreStartResumeFull"),
2734                        new Object[]{tsa.getDataWhen()});
2735            case TransitSectionAction.ESTOP:
2736                return rbx.getString("EStopFull");
2737            case TransitSectionAction.TERMINATETRAIN:
2738                return rbx.getString("TerminateTrain");
2739            case TransitSectionAction.FORCEALLOCATEPASSSAFESECTION:
2740                return rbx.getString("ForcePassNextSafe");
2741            case TransitSectionAction.LOADTRAININFO:
2742                switch (tsa.getDataWhat2()) {
2743                    case TransitSectionAction.LOCOADDRESSTYPEROSTER:
2744                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoRosterFull"),
2745                                new Object[]{tsa.getStringWhat(),tsa.getStringWhat2()});
2746                    case TransitSectionAction.LOCOADDRESSTYPENUMBER:
2747                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoNumberFull"),
2748                                new Object[]{tsa.getStringWhat(),tsa.getStringWhat2()});
2749                    case TransitSectionAction.LOCOADDRESSTYPECURRENT:
2750                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoCurrentFull"),
2751                                new Object[]{tsa.getStringWhat()});
2752                    case TransitSectionAction.LOCOADDRESSTYPEDEFAULT:
2753                    default:
2754                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoDefaultFull"),
2755                                new Object[]{tsa.getStringWhat()});
2756                 }
2757            case TransitSectionAction.SELECTWHAT:
2758                return rbx.getString("SelectWhat");
2759            default:
2760                log.warn("Unhandled transit section action What code: {}", tsa.getWhatCode());
2761                break;
2762        }
2763        return "WHAT";
2764    }
2765
2766    private String getSectionNameByRow(int r) {
2767        return sectionList.get(r).getDisplayName();
2768    }
2769
2770    /**
2771     * Table model for Sections in Create/Edit Transit window.
2772     */
2773    public class SectionTableModel extends javax.swing.table.AbstractTableModel implements
2774            java.beans.PropertyChangeListener {
2775
2776        public static final int SEQUENCE_COLUMN = 0;
2777        public static final int SECTIONNAME_COLUMN = 1;
2778        public static final int ACTION_COLUMN = 2;
2779        public static final int SEC_DIRECTION_COLUMN = 3;
2780        public static final int ALTERNATE_COLUMN = 4;
2781        public static final int SAFE_COLUMN = 5;
2782        public static final int STOPALLOCATING_SENSOR = 6;
2783        public static final int FWD_STOPPING_LENGTH = 7;
2784        public static final int REV_STOPPING_LENGTH = 8;
2785        public static final int NUMBER_OF_COLUMNS = 9;
2786
2787        public SectionTableModel() {
2788            super();
2789            addPcl();
2790        }
2791
2792        final void addPcl(){
2793            sectionManager.addPropertyChangeListener(this);
2794        }
2795
2796        @Override
2797        public void propertyChange(java.beans.PropertyChangeEvent e) {
2798            if (Manager.PROPERTY_LENGTH.equals(e.getPropertyName())) {
2799                // a new NamedBean is available in the manager
2800                fireTableDataChanged();
2801            }
2802            if (e.getSource() instanceof SensorManager
2803                    && SensorManager.PROPERTY_DISPLAY_LIST_NAME.equals(e.getPropertyName())) {
2804                updateSensorList();
2805            }
2806        }
2807
2808        @Override
2809        public Class<?> getColumnClass(int c) {
2810            switch (c) {
2811                case ACTION_COLUMN:
2812                    return JButton.class;
2813                case SAFE_COLUMN:
2814                    return Boolean.class;
2815                case STOPALLOCATING_SENSOR:
2816                    return JComboBox.class;
2817                case FWD_STOPPING_LENGTH:
2818                case REV_STOPPING_LENGTH:
2819                    return JSpinner.class;
2820                default:
2821                    return super.getColumnClass(c);
2822            }
2823        }
2824
2825        @Override
2826        public int getColumnCount() {
2827            return NUMBER_OF_COLUMNS;
2828        }
2829
2830        @Override
2831        public int getRowCount() {
2832            return (sectionList.size());
2833        }
2834
2835        @Override
2836        public boolean isCellEditable(int r, int c) {
2837            switch (c) {
2838                case ACTION_COLUMN:
2839                case SAFE_COLUMN:
2840                case STOPALLOCATING_SENSOR:
2841                case FWD_STOPPING_LENGTH:
2842                case REV_STOPPING_LENGTH:
2843                    return true;
2844                default:
2845                    return false;
2846            }
2847        }
2848
2849        @Override
2850        public String getColumnName(int col) {
2851            switch (col) {
2852                case SEQUENCE_COLUMN:
2853                    return rbx.getString("SequenceColName");
2854                case SECTIONNAME_COLUMN:
2855                    return Bundle.getMessage("BeanNameSection");
2856                case ACTION_COLUMN:
2857                    return rbx.getString("ActionColName");
2858                case SEC_DIRECTION_COLUMN:
2859                    return rbx.getString("DirectionColName");
2860                case ALTERNATE_COLUMN:
2861                    return rbx.getString("AlternateColName");
2862                case SAFE_COLUMN:
2863                    return rbx.getString("SafeColName");
2864                case STOPALLOCATING_SENSOR:
2865                    return rbx.getString("StopAllocationColName");
2866                case FWD_STOPPING_LENGTH:
2867                    return rbx.getString("FwdStopDistance");
2868                case REV_STOPPING_LENGTH:
2869                    return rbx.getString("RevStopDistance");
2870               default:
2871                    return "";
2872            }
2873        }
2874
2875        @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
2876                                justification="better to keep cases in column order rather than to combine")
2877        public int getPreferredWidth(int col) {
2878            switch (col) {
2879                case SEQUENCE_COLUMN:
2880                    return new JTextField(8).getPreferredSize().width;
2881                case SECTIONNAME_COLUMN:
2882                    return new JTextField(17).getPreferredSize().width;
2883                case ACTION_COLUMN:
2884                    return new JTextField(12).getPreferredSize().width;
2885                case SEC_DIRECTION_COLUMN:
2886                    return new JTextField(12).getPreferredSize().width;
2887                case ALTERNATE_COLUMN:
2888                    return new JTextField(12).getPreferredSize().width;
2889                case SAFE_COLUMN:
2890                    return new JTextField(4).getPreferredSize().width;
2891                case STOPALLOCATING_SENSOR:
2892                    return new JTextField(12).getPreferredSize().width;
2893                case FWD_STOPPING_LENGTH:
2894                    return new JTextField(12).getPreferredSize().width;
2895                case REV_STOPPING_LENGTH:
2896                    return new JTextField(12).getPreferredSize().width;
2897               default:
2898                    // fall through
2899                    break;
2900            }
2901            return new JTextField(5).getPreferredSize().width;
2902        }
2903
2904        @Override
2905        public Object getValueAt(int r, int c) {
2906            int rx = r;
2907            if (rx > sectionList.size()) {
2908                return null;
2909            }
2910            switch (c) {
2911                case SEQUENCE_COLUMN:
2912                    return ("" + sequence.get(rx));
2913                case SECTIONNAME_COLUMN:
2914                    return (getSectionNameByRow(rx));
2915                case ACTION_COLUMN:
2916                    return rbx.getString("AddEditActions");
2917                case SEC_DIRECTION_COLUMN:
2918                    if (direction.get(rx) == Section.FORWARD) {
2919                        return rbx.getString("SectionForward");
2920                    } else if (direction.get(rx) == Section.REVERSE) {
2921                        return rbx.getString("SectionReverse");
2922                    }
2923                    return Bundle.getMessage("BeanStateUnknown");
2924                case ALTERNATE_COLUMN:
2925                    if (alternate.get(rx)) {
2926                        return rbx.getString("Alternate");
2927                    }
2928                    return rbx.getString("Primary");
2929                case SAFE_COLUMN:
2930                    return safe.get(rx);
2931                case STOPALLOCATING_SENSOR:
2932                    String sensor = sensorStopAllocation.get(rx);
2933                    JComboBox<String> cb = new JComboBox<>(sensorList);
2934                    JComboBoxUtil.setupComboBoxMaxRows(cb);
2935                    String name = (sensor != null) ? sensor : "";
2936                    cb.setSelectedItem(name);
2937                    return cb;
2938                case FWD_STOPPING_LENGTH:
2939                    JSpinner jf = new JSpinner(new SpinnerNumberModel(Float.valueOf(1.0f), Float.valueOf(0.0f), Float.valueOf(2.0f), Float.valueOf(0.05f)));
2940                    jf.setEditor(new JSpinner.NumberEditor(jf, "##0.0# %"));
2941                    jf.setValue(fwdStopPerCent.get(rx));
2942                    return jf;
2943                case REV_STOPPING_LENGTH:
2944                    JSpinner jr = new JSpinner(new SpinnerNumberModel(Float.valueOf(1.0f), Float.valueOf(0.0f), Float.valueOf(2.0f), Float.valueOf(0.05f)));
2945                    jr.setEditor(new JSpinner.NumberEditor(jr, "##0.0# %"));
2946                    jr.setValue(revStopPerCent.get(rx));
2947                    return jr;
2948                default:
2949                    return Bundle.getMessage("BeanStateUnknown");
2950            }
2951        }
2952
2953        @Override
2954        public void setValueAt(Object value, int row, int col) {
2955            switch (col) {
2956                case ACTION_COLUMN:
2957                    addEditActionsPressed(row);
2958                    break;
2959                case SAFE_COLUMN:
2960                    boolean val = ((Boolean) value);
2961                    safe.set(row, val); // use checkbox to show Safe
2962                    break;
2963                case STOPALLOCATING_SENSOR:
2964                    JComboBox<?> cb = (JComboBox<?>) value;
2965                    if (cb.getSelectedIndex() < 0) {
2966                        sensorStopAllocation.set(row, "");
2967                    } else {
2968                        sensorStopAllocation.set(row, (String) cb.getSelectedItem());
2969                    }
2970                    break;
2971                case FWD_STOPPING_LENGTH:
2972                    fwdStopPerCent.set(row, (Float)value);
2973                    break;
2974                case REV_STOPPING_LENGTH:
2975                    revStopPerCent.set(row, (Float)value);
2976                    break;
2977                default:
2978                    break;
2979            }
2980        }
2981
2982        public void dispose(){
2983            sectionManager.removePropertyChangeListener(this);
2984        }
2985    }
2986
2987    private void updateSensorList() {
2988        Set<Sensor> nameSet = InstanceManager.getDefault(SensorManager.class).getNamedBeanSet();
2989        String[] displayList = new String[nameSet.size()];
2990        int i = 0;
2991        for (Sensor nBean : nameSet) {
2992            if (nBean != null) {
2993                displayList[i++] = nBean.getDisplayName();
2994            }
2995        }
2996        java.util.Arrays.sort(displayList, new jmri.util.AlphanumComparator());
2997        sensorList = new String[displayList.length + 1];
2998        sensorList[0] = "";
2999        i = 1;
3000        for (String name : displayList) {
3001            sensorList[i] = name;
3002            i++;
3003        }
3004    }
3005
3006
3007    /**
3008     * Table model for Actions in Special Actions window. Currently shows max. 5
3009     * rows.
3010     */
3011    public class SpecialActionTableModel extends javax.swing.table.AbstractTableModel implements
3012            java.beans.PropertyChangeListener {
3013
3014        public static final int WHEN_COLUMN = 0;
3015        public static final int WHAT_COLUMN = 1;
3016        public static final int EDIT_COLUMN = 2;
3017        public static final int REMOVE_COLUMN = 3;
3018
3019        public SpecialActionTableModel() {
3020            super();
3021            addPcl();
3022        }
3023
3024        final void addPcl(){
3025            sectionManager.addPropertyChangeListener(this);
3026        }
3027
3028        @Override
3029        public void propertyChange(java.beans.PropertyChangeEvent e) {
3030            if ( Manager.PROPERTY_LENGTH.equals(e.getPropertyName())) {
3031                // a new NamedBean is available in the manager
3032                fireTableDataChanged();
3033            }
3034        }
3035
3036        @Override
3037        public Class<?> getColumnClass(int c) {
3038            switch (c) {
3039                case EDIT_COLUMN:
3040                case REMOVE_COLUMN:
3041                    return JButton.class;
3042                case WHEN_COLUMN:
3043                case WHAT_COLUMN:
3044                default:
3045                    return String.class;
3046            }
3047        }
3048
3049        @Override
3050        public int getColumnCount() {
3051            return REMOVE_COLUMN + 1;
3052        }
3053
3054        @Override
3055        public int getRowCount() {
3056            return (action.get(activeRow).size());
3057        }
3058
3059        @Override
3060        public boolean isCellEditable(int r, int c) {
3061            switch (c) {
3062                case EDIT_COLUMN:
3063                case REMOVE_COLUMN:
3064                    return true;
3065                default:
3066                    return false;
3067            }
3068        }
3069
3070        @Override
3071        public String getColumnName(int col) {
3072            switch (col) {
3073                case WHEN_COLUMN:
3074                    return rbx.getString("WhenColName");
3075                case WHAT_COLUMN:
3076                    return rbx.getString("WhatColName");
3077                default:
3078                    return "";
3079            }
3080        }
3081
3082        public int getPreferredWidth(int col) {
3083            switch (col) {
3084                case WHEN_COLUMN:
3085                case WHAT_COLUMN:
3086                    return new JTextField(50).getPreferredSize().width;
3087                case EDIT_COLUMN:
3088                case REMOVE_COLUMN:
3089                default:
3090                    return new JTextField(8).getPreferredSize().width;
3091            }
3092        }
3093
3094        @Override
3095        public Object getValueAt(int r, int c) {
3096            int rx = r;
3097            if (rx > action.get(activeRow).size()) {
3098                return null;
3099            }
3100            switch (c) {
3101                case WHEN_COLUMN:
3102                    return (getWhenText(rx));
3103                case WHAT_COLUMN:
3104                    return (getWhatText(rx));
3105                case EDIT_COLUMN:
3106                    return Bundle.getMessage("ButtonEdit");
3107                case REMOVE_COLUMN:
3108                    return Bundle.getMessage("ButtonDelete");
3109                default:
3110                    return Bundle.getMessage("BeanStateUnknown"); // normally not in use
3111            }
3112        }
3113
3114        @Override
3115        public void setValueAt(Object value, int row, int col) {
3116            if (col == EDIT_COLUMN) {
3117                // set up to edit
3118                editAction(row);
3119            }
3120            else if (col == REMOVE_COLUMN) {
3121                deleteAction(row);
3122            }
3123        }
3124
3125        public void dispose(){
3126            sectionManager.removePropertyChangeListener(this);
3127        }
3128    }
3129
3130    @Override
3131    protected String getClassName() {
3132        return TransitTableAction.class.getName();
3133    }
3134
3135    @Override
3136    public String getClassDescription() {
3137        return Bundle.getMessage("TitleTransitTable");
3138    }
3139
3140    public class JSpinnerCellRenderer extends DefaultTableCellRenderer {
3141
3142        public JSpinnerCellRenderer() {
3143        }
3144
3145        @Override
3146        public Component getTableCellRendererComponent(JTable table, Object value,
3147                                                       boolean isSelected, boolean hasFocus,
3148                                                       int row, int column) {
3149            if (value instanceof JSpinner) {
3150                switch (column) {
3151                    case SectionTableModel.FWD_STOPPING_LENGTH:
3152                        ((JSpinner) value).setValue(fwdStopPerCent.get(row));
3153                        break;
3154                    case SectionTableModel.REV_STOPPING_LENGTH:
3155                        ((JSpinner) value).setValue(revStopPerCent.get(row));
3156                        break;
3157                    default:
3158                        log.error("getTableCellEditorComponent col[{}] is bad.",column);
3159                }
3160            }
3161            return (Component)value;
3162        }
3163    }
3164
3165    public class JSpinnerCellEditor extends AbstractCellEditor implements TableCellEditor {
3166        private JSpinner spinner;
3167
3168        public JSpinnerCellEditor() {
3169        }
3170
3171        @Override
3172        public Component getTableCellEditorComponent(JTable table, Object value,
3173                                                       boolean isSelected, int row, int column) {
3174            spinner = (JSpinner) value;
3175            if (value instanceof JSpinner) {
3176                switch (column) {
3177                    case SectionTableModel.FWD_STOPPING_LENGTH:
3178                        ((JSpinner) value).setValue(fwdStopPerCent.get(row));
3179                        break;
3180                    case SectionTableModel.REV_STOPPING_LENGTH:
3181                        ((JSpinner) value).setValue(revStopPerCent.get(row));
3182                        break;
3183                    default:
3184                        log.error("getTableCellEditorComponent col[{}] is bad.",column);
3185                }
3186            }
3187            return (Component)value;
3188        }
3189
3190        @Override
3191        public Object getCellEditorValue() {
3192            return spinner.getValue();
3193        }
3194    }
3195    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TransitTableAction.class);
3196
3197}