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