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                break;
1858            case TransitSectionAction.LOADTRAININFO:
1859                rosterComboBox.update();
1860                String[] names = new TrainInfoFile().getTrainInfoFileNames();
1861                trainInfoComboBox.removeAllItems();
1862                for (String fn: names) {
1863                    trainInfoComboBox.addItem(fn);
1864                    if (fn.equals(tWhatString)) {
1865                        trainInfoComboBox.setSelectedItem(fn);
1866                    }
1867                }
1868                locoAddress.setText(Integer.toString(tWhatData1));
1869                switch (tWhatData2) {
1870                    case TransitSectionAction.LOCOADDRESSTYPEROSTER:
1871                        locoAddressRoster.setSelected(true);
1872                        rosterComboBox.setSelectedItem(tWhatString2);
1873                        rosterComboBox.setVisible(true);
1874                        locoAddress.setVisible(false);
1875                        break;
1876                    case TransitSectionAction.LOCOADDRESSTYPENUMBER:
1877                        locoAddressNumber.setSelected(true);
1878                        locoAddress.setText(tWhatString2);
1879                        rosterComboBox.setVisible(false);
1880                        locoAddress.setVisible(true);
1881                        break;
1882                    case TransitSectionAction.LOCOADDRESSTYPECURRENT:
1883                        locoAddressCurrent.setSelected(true);
1884                        locoAddress.setText("");
1885                        rosterComboBox.setVisible(false);
1886                        locoAddress.setVisible(false);
1887                        break;
1888                    case TransitSectionAction.LOCOADDRESSTYPEDEFAULT:
1889                    default:
1890                        locoAddressDefault.setSelected(true);
1891                        rosterComboBox.setVisible(false);
1892                        locoAddress.setVisible(false);
1893                        locoAddress.setText("");
1894                        break;
1895                }
1896                panelLoadTrainInfo.setVisible(true);
1897                break;
1898            case TransitSectionAction.PAUSE:
1899                if (getWhenMenuCode((String)whenBox.getSelectedItem()) == TransitSectionAction.PRESTARTDELAY) {
1900                    panelDelay.setEnabled(false);
1901                }
1902                whatMinuteSpinner1.setModel(new SpinnerNumberModel(1, 1, 65500, 1));
1903                if (editActionMode) {
1904                    whatMinuteSpinner1.setValue(Math.max(curTSA.getDataWhat1(), 1));
1905                }
1906                whatMinuteSpinner1.setVisible(true);
1907                whatMinuteSpinner1.setToolTipText(rbx.getString("HintPauseData"));
1908                break;
1909            case TransitSectionAction.SETMAXSPEED:
1910            case TransitSectionAction.SETCURRENTSPEED:
1911            case TransitSectionAction.RAMPTRAINSPEED:
1912                if (editActionMode) {
1913                    float maxPerc = Math.max(0.01f * curTSA.getDataWhat1(), 0.0f);
1914                    whatPercentSpinner.setValue(maxPerc);
1915                }
1916                whatPercentSpinner.setVisible(true);
1917                whatPercentSpinner.setToolTipText(rbx.getString("HintSetSpeedData1"));
1918                break;
1919            case TransitSectionAction.TOMANUALMODE:
1920                if (editActionMode) {
1921                    doneSensorComboBox.setSelectedItemByName(curTSA.getStringWhat());
1922                }
1923                doneSensorLabel.setVisible(true);
1924                doneSensorComboBox.setVisible(true);
1925                doneSensorComboBox.setToolTipText(rbx.getString("HintDoneSensor"));
1926                break;
1927            case TransitSectionAction.SETLIGHT:
1928                onButton.setVisible(true);
1929                offButton.setVisible(true);
1930                onButton.setToolTipText(rbx.getString("HintSetLight"));
1931                offButton.setToolTipText(rbx.getString("HintSetLight"));
1932                break;
1933            case TransitSectionAction.STARTBELL:
1934                break;
1935            case TransitSectionAction.STOPBELL:
1936                break;
1937            case TransitSectionAction.SOUNDHORN:
1938                whatMinuteSpinner1.setValue(100);
1939                whatMinuteSpinner1.setModel(new SpinnerNumberModel(100, 100, 65500, 1));
1940                if (editActionMode) {
1941                    whatMinuteSpinner1.setValue(curTSA.getDataWhat1());
1942                }
1943                if ((Integer) whatMinuteSpinner1.getValue() < 100) {
1944                    whatMinuteSpinner1.setValue(100); // might result from user changing from PAUSE to SOUNDHORN
1945                }
1946                whatMinuteSpinner1.setVisible(true);
1947                whatMinuteSpinner1.setToolTipText(rbx.getString("HintSoundHornData1"));
1948                break;
1949            case TransitSectionAction.SOUNDHORNPATTERN:
1950                whatMinuteSpinner1.setValue(100);
1951                whatMinuteSpinner1.setModel(new SpinnerNumberModel(100, 100, 65500, 1));
1952                // whatMinuteSpinner2 model never changes
1953                if (editActionMode) {
1954                    whatMinuteSpinner1.setValue(curTSA.getDataWhat1());
1955                    whatMinuteSpinner2.setValue(Math.max(curTSA.getDataWhat2(), 100));
1956                    // might result from user changing from sth.else to SOUNDHORNPATTERN
1957                }
1958                if ((Integer) whatMinuteSpinner1.getValue() < 100) {
1959                    whatMinuteSpinner1.setValue(100); // might result from user changing from PAUSE to SOUNDHORNPATTERN
1960                }
1961                whatMinuteSpinner1.setVisible(true);
1962                whatMinuteSpinner1.setToolTipText(rbx.getString("HintSoundHornPatternData1"));
1963                whatMinuteSpinner2.setVisible(true);
1964                whatMinuteSpinner2.setToolTipText(rbx.getString("HintSoundHornPatternData2"));
1965                whatStringField.setVisible(true);
1966                break;
1967            case TransitSectionAction.LOCOFUNCTION:
1968                if (editActionMode) {
1969                    locoFunctionSpinner.setValue(curTSA.getDataWhat1());
1970                }
1971                locoFunctionSpinner.setVisible(true);
1972                locoFunctionSpinner.setToolTipText(rbx.getString("HintLocoFunctionData1"));
1973                onButton.setVisible(true);
1974                offButton.setVisible(true);
1975                onButton.setToolTipText(rbx.getString("HintLocoFunctionOnOff"));
1976                offButton.setToolTipText(rbx.getString("HintLocoFunctionOnOff"));
1977                break;
1978            case TransitSectionAction.SETSENSORACTIVE:
1979            case TransitSectionAction.SETSENSORINACTIVE:
1980                if (editActionMode) {
1981                    doneSensorComboBox.setSelectedItemByName(curTSA.getStringWhat());
1982                }
1983                doneSensorComboBox.setVisible(true);
1984                doneSensorComboBox.setToolTipText(rbx.getString("HintSensorEntry"));
1985                break;
1986            case TransitSectionAction.HOLDSIGNAL:
1987            case TransitSectionAction.RELEASESIGNAL:
1988                if (editActionMode) {
1989                    SignalMast sm = InstanceManager.getDefault(SignalMastManager.class).getSignalMast(curTSA.getStringWhat());
1990                    if (sm != null) { // name is an existing mast
1991                        signalMastComboBox.setSelectedItemByName(curTSA.getStringWhat());
1992                    } else {
1993                        SignalHead sh = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(curTSA.getStringWhat());
1994                        if (sh != null) { // name is an existing head
1995                            signalHeadComboBox.setSelectedItemByName(curTSA.getStringWhat());
1996                        }
1997                    }
1998                }
1999                signalPanel.setVisible(true);
2000                break;
2001            case TransitSectionAction.ESTOP:
2002            default:
2003                log.debug("Unhandled transit section action: {}", code); // causes too much noise, no harm done hiding it
2004                break;
2005        }
2006        addEditActionFrame.pack();
2007        addEditActionFrame.setVisible(true);
2008    }
2009
2010    private void updateTrainInfoAddressFields(ActionEvent e) {
2011        if (!((JRadioButton)e.getSource()).isSelected() ) {
2012            return;
2013        }
2014        if (e.getSource() == locoAddressRoster) {
2015            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEROSTER;
2016            rosterComboBox.setVisible(true);
2017            locoAddress.setVisible(false);
2018        } else if (e.getSource() == locoAddressNumber) {
2019            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPENUMBER;
2020            rosterComboBox.setVisible(false);
2021            locoAddress.setVisible(true);
2022        } else if (e.getSource() == locoAddressDefault) {
2023            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEDEFAULT;
2024            rosterComboBox.setVisible(false);
2025            locoAddress.setVisible(false);
2026        } else if (e.getSource() == locoAddressCurrent) {
2027            tWhatData2 = TransitSectionAction.LOCOADDRESSTYPECURRENT;
2028            rosterComboBox.setVisible(false);
2029            locoAddress.setVisible(false);
2030        } else {
2031            log.warn("Unknown button Source");
2032        }
2033    }
2034
2035    // temporary action variables
2036    private int tWhen = 0;
2037    private int tWhenData = 0;
2038    private String tWhenString = "";
2039    private int tWhat = 0;
2040    private int tWhatData1 = 0;
2041    private int tWhatData2 = 0;
2042    private String tWhatString = "";
2043    private String tWhatString2 = "";
2044
2045    /**
2046     * Handle button presses in Add/Edit Transit Action window.
2047     *
2048     * @param e the event seen
2049     */
2050    private void createActionPressed(ActionEvent e) {
2051        if ((!validateWhenData()) || (!validateWhatData())) {
2052            return;
2053        }
2054        // entered data is OK, create a special action
2055        curTSA = new TransitSectionAction(tWhen, tWhat, tWhenData, tWhatData1, tWhatData2, tWhenString, tWhatString, tWhatString2);
2056        List<TransitSectionAction> list = action.get(activeRow);
2057        list.add(curTSA);
2058        actionTableModel.fireTableDataChanged();
2059        addEditActionFrame.setVisible(false);
2060        addEditActionFrame.dispose();
2061        addEditActionFrame = null;
2062    }
2063
2064    private void updateActionPressed(ActionEvent e) {
2065        if ((!validateWhenData()) || (!validateWhatData())) {
2066            return;
2067        }
2068        // entered data is OK, update the current special action
2069        curTSA.setWhenCode(tWhen);
2070        curTSA.setWhatCode(tWhat);
2071        curTSA.setDataWhen(tWhenData);
2072        curTSA.setDataWhat1(tWhatData1);
2073        curTSA.setDataWhat2(tWhatData2);
2074        curTSA.setStringWhen(tWhenString);
2075        curTSA.setStringWhat(tWhatString);
2076        curTSA.setStringWhat2(tWhatString2);
2077        actionTableModel.fireTableDataChanged();
2078        addEditActionFrame.setVisible(false);
2079        addEditActionFrame.dispose();
2080        addEditActionFrame = null;
2081    }
2082
2083    private void cancelAddEditActionPressed(ActionEvent e) {
2084        addEditActionFrame.setVisible(false);
2085        addEditActionFrame.dispose();
2086        addEditActionFrame = null;
2087    }
2088
2089    private boolean validateWhenData() {
2090        tWhen = getWhenMenuCode((String)whenBox.getSelectedItem());
2091        tWhenData = (int)whenDataSpinnerInt.getValue();
2092        tWhenString = "";
2093        if (tWhen == TransitSectionAction.PRESTARTDELAY ) {
2094            // must have a delay
2095            if (tWhenData <1 ) {
2096                return false;
2097            }
2098        }
2099        if ((tWhen == TransitSectionAction.SENSORACTIVE) || (tWhen == TransitSectionAction.SENSORINACTIVE)) {
2100            if (whenSensorComboBox.getSelectedIndex() != 0) { // it's optional, so might be 0
2101                tWhenString = whenSensorComboBox.getSelectedItemSystemName();
2102            }
2103            if (!validateSensor(tWhenString, true)) {
2104                return false;
2105            }
2106        }
2107        if ((tWhen == TransitSectionAction.BLOCKENTRY) || (tWhen == TransitSectionAction.BLOCKEXIT)) {
2108            tWhenString = blockList.get(blockBox.getSelectedIndex()).getSystemName();
2109        }
2110        return true;
2111    }
2112
2113    private boolean validateSensor(String sName, boolean when) {
2114        // check if anything entered
2115        if (sName.length() < 1) {
2116            // no sensor selected
2117            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("NoSensorError")),
2118                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2119            return false;
2120        }
2121        // get the sensor corresponding to this name
2122        Sensor s = InstanceManager.sensorManagerInstance().getSensor(sName);
2123        if (s == null) {
2124            // There is no sensor corresponding to this name
2125            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("SensorEntryError")),
2126                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2127            return false;
2128        }
2129        if (!sName.equals(s.getUserName())) {
2130            if (when) {
2131                tWhenString = sName;
2132            } else {
2133                tWhatString = sName;
2134            }
2135        }
2136        return true;
2137    }
2138
2139    private boolean validateSignal(String sName, boolean when) {
2140        // check if anything is selected
2141        if (sName.length() < 1) {
2142            // no signal selected
2143            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("NoSignalError")),
2144                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2145            return false;
2146        }
2147        // get the signalMast or signalHead corresponding to this name
2148        SignalMast sm = InstanceManager.getDefault(SignalMastManager.class).getSignalMast(sName);
2149        SignalHead sh = null;
2150        if (sm == null) {
2151            sh = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(sName);
2152        }
2153        if (sm == null && sh == null) {
2154            // There is no signal corresponding to this name
2155            JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("SignalEntryError")),
2156                    Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2157            return false;
2158        }
2159        return true;
2160    }
2161
2162    /**
2163     * Validate entered data for selected Action. Converted to use JSpinners
2164     * where applicable, 2017.
2165     *
2166     * @return true if data entered into field whatStringField is valid for selected Action type tWhat
2167     */
2168    private boolean validateWhatData() {
2169        tWhat = getWhatMenuCode((String)whatBox.getSelectedItem());
2170        tWhatData1 = 0;
2171        tWhatData2 = 0;
2172        tWhatString = "";
2173        switch (tWhat) {
2174            case TransitSectionAction.SETMAXSPEED:
2175            case TransitSectionAction.SETCURRENTSPEED:
2176            case TransitSectionAction.RAMPTRAINSPEED:
2177                tWhatData1 = Math.round(100 * (float) whatPercentSpinner.getValue());
2178                break;
2179            case TransitSectionAction.TOMANUALMODE:
2180                tWhatString="";
2181                if (doneSensorComboBox.getSelectedIndex() >= 0) { // it's optional, so might be -1
2182                    tWhatString = doneSensorComboBox.getSelectedItemSystemName(); // sensor system name
2183                }
2184                if (tWhatString.length() >= 1) {
2185                    if (!validateSensor(tWhatString, false)) {
2186                        tWhatString = "";
2187                    }
2188                }
2189                break;
2190            case TransitSectionAction.SETLIGHT:
2191                tWhatString = "On"; // NOI18N
2192                if (offButton.isSelected()) {
2193                    tWhatString = "Off"; // NOI18N
2194                }
2195                break;
2196            case TransitSectionAction.STARTBELL:
2197            case TransitSectionAction.STOPBELL:
2198            case TransitSectionAction.TERMINATETRAIN:
2199                break;
2200            case TransitSectionAction.LOADTRAININFO:
2201                if (trainInfoComboBox.getSelectedIndex() < 0 ) {
2202                    JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingTrainInfoFile")),
2203                            Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2204                    return false;
2205                }
2206                tWhatString = (String)trainInfoComboBox.getSelectedItem();
2207                if (locoAddressRoster.isSelected()) {
2208                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEROSTER;
2209                    // the first item is "select..."
2210                    if (rosterComboBox.getSelectedIndex() < 1) {
2211                        JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingRosterEntryOrLocoAddress")),
2212                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2213                        return false;
2214                    }
2215                    tWhatString2 =((RosterEntry) rosterComboBox.getSelectedItem()).getId();
2216                } else if (locoAddressNumber.isSelected()) {
2217                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPENUMBER;
2218                    tWhatString2 = locoAddress.getText();
2219                    if ((tWhatString2 == null) || tWhatString2.isEmpty() || (tWhatString2.length() < 1)) {
2220                        JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingRosterEntryOrLocoAddress")),
2221                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2222                        return false;
2223                    }
2224                } else if (locoAddressDefault.isSelected()) {
2225                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPEDEFAULT;
2226                    tWhatString2 = "";
2227                } else if (locoAddressCurrent.isSelected()) {
2228                    tWhatData2 = TransitSectionAction.LOCOADDRESSTYPECURRENT;
2229                    tWhatString2 = "";
2230                } else {
2231                    JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("UnKnownlocoaddresstype")),
2232                            Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2233                    return false;
2234                }
2235                break;
2236            case TransitSectionAction.PAUSE:
2237            case TransitSectionAction.SOUNDHORN:
2238                tWhatData1 = (Integer) whatMinuteSpinner1.getValue();
2239                break;
2240            case TransitSectionAction.SOUNDHORNPATTERN:
2241                tWhatData1 = (Integer) whatMinuteSpinner1.getValue();
2242                tWhatData2 = (Integer) whatMinuteSpinner2.getValue();
2243                tWhatString = whatStringField.getText();
2244                if ((tWhatString == null) || tWhatString.isEmpty() || (tWhatString.length() < 1)) {
2245                    JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("MissingPattern")),
2246                            Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2247                    return false;
2248                }
2249                tWhatString = tWhatString.trim().toLowerCase();
2250                for (int i = 0; i < tWhatString.length(); i++) {
2251                    char c = tWhatString.charAt(i);
2252                    if ((c != 's') && (c != 'l')) {
2253                        JmriJOptionPane.showMessageDialog(addEditActionFrame, (rbx.getString("ErrorPattern")),
2254                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
2255                        return false;
2256                    }
2257                }
2258                whatStringField.setText(tWhatString); // re-enter normalized value in display field
2259                break;
2260            case TransitSectionAction.LOCOFUNCTION:
2261                tWhatData1 = (Integer) locoFunctionSpinner.getValue();
2262                tWhatString = "On"; // NOI18N
2263                if (offButton.isSelected()) {
2264                    tWhatString = "Off"; // NOI18N
2265                }
2266                break;
2267            case TransitSectionAction.SETSENSORACTIVE:
2268            case TransitSectionAction.SETSENSORINACTIVE:
2269                if (doneSensorComboBox.getSelectedIndex() != 0) {
2270                    tWhatString = doneSensorComboBox.getSelectedItemSystemName();
2271                }
2272                if (!validateSensor(tWhatString, false)) {
2273                    return false;
2274                }
2275                break;
2276            case TransitSectionAction.HOLDSIGNAL:
2277            case TransitSectionAction.RELEASESIGNAL:
2278                if (signalMastComboBox.getSelectedIndex() != 0) {
2279                    tWhatString = signalMastComboBox.getSelectedItemSystemName();
2280                } else if (signalHeadComboBox.getSelectedIndex() != 0) {
2281                    tWhatString = signalHeadComboBox.getSelectedItemSystemName();
2282                }
2283                if (!validateSignal(tWhatString, false)) {
2284                    return false;
2285                }
2286                break;
2287            case TransitSectionAction.PRESTARTRESUME:
2288                break;
2289            default:
2290                log.warn("Unhandled transit section action code: {}", tWhat);
2291                break;
2292        }
2293        return true;
2294    }
2295
2296    // initialize combos for add/edit action window
2297    private void initializeWhenBox() {
2298        whenBox.removeAllItems();
2299        for (int i = 0; i <= TransitSectionAction.NUM_WHENS; i++) {
2300            whenBox.addItem(getWhenMenuText(i));
2301        }
2302    }
2303
2304    private String getWhenMenuText(int i) {
2305        switch (i) {
2306            case TransitSectionAction.ENTRY:
2307                return rbx.getString("OnEntry");
2308            case TransitSectionAction.EXIT:
2309                return rbx.getString("OnExit");
2310            case TransitSectionAction.BLOCKENTRY:
2311                return rbx.getString("OnBlockEntry");
2312            case TransitSectionAction.BLOCKEXIT:
2313                return rbx.getString("OnBlockExit");
2314            case TransitSectionAction.TRAINSTOP:
2315                return rbx.getString("TrainStop");
2316            case TransitSectionAction.TRAINSTART:
2317                return rbx.getString("TrainStart");
2318            case TransitSectionAction.SENSORACTIVE:
2319                return rbx.getString("OnSensorActive");
2320            case TransitSectionAction.SENSORINACTIVE:
2321                return rbx.getString("OnSensorInactive");
2322            case TransitSectionAction.PRESTARTDELAY:
2323                return rbx.getString("PreStartDelay");
2324            case TransitSectionAction.PRESTARTACTION:
2325                return rbx.getString("PreStartAction");
2326            case TransitSectionAction.SELECTWHEN:
2327                return rbx.getString("SelectWhen");
2328            default:
2329                log.warn("Unhandled transit section when code: {}", i);
2330                return rbx.getString("SelectWhen");
2331        }
2332    }
2333
2334    private int getWhenMenuCode(String s) {
2335        if (s.equals(rbx.getString("OnEntry"))) {
2336            return TransitSectionAction.ENTRY;
2337        }
2338        if (s.equals(rbx.getString("OnExit"))) {
2339            return TransitSectionAction.EXIT;
2340        }
2341        if (s.equals(rbx.getString("OnBlockEntry"))) {
2342            return TransitSectionAction.BLOCKENTRY;
2343        }
2344        if (s.equals(rbx.getString("OnBlockExit"))) {
2345            return TransitSectionAction.BLOCKEXIT;
2346        }
2347        if (s.equals(rbx.getString("TrainStop"))) {
2348            return TransitSectionAction.TRAINSTOP;
2349        }
2350        if (s.equals(rbx.getString("TrainStart"))) {
2351            return TransitSectionAction.TRAINSTART;
2352        }
2353        if (s.equals(rbx.getString("OnSensorActive"))) {
2354            return TransitSectionAction.SENSORACTIVE;
2355        }
2356        if (s.equals(rbx.getString("OnSensorInactive"))) {
2357            return TransitSectionAction.SENSORINACTIVE;
2358        }
2359        if (s.equals(rbx.getString("PreStartDelay"))) {
2360            return TransitSectionAction.PRESTARTDELAY;
2361        }
2362        if (s.equals(rbx.getString("PreStartAction"))) {
2363            return TransitSectionAction.PRESTARTACTION;
2364        }
2365        return TransitSectionAction.SELECTWHEN;
2366    }
2367
2368    private void initializeWhatBox(int code) {
2369        whatBox.removeAllItems();
2370        List<Integer> excludeCodes = new ArrayList<>();
2371        List<Integer> includeCodes = new ArrayList<>();
2372        if (code == TransitSectionAction.PRESTARTACTION) {
2373            // exclude speed changing as that messes up the prestart.List<Section> sectionList = new ArrayList<>();
2374            excludeCodes = new ArrayList<>(Arrays.asList(TransitSectionAction.SETMAXSPEED, TransitSectionAction.SETCURRENTSPEED,
2375                            TransitSectionAction.RAMPTRAINSPEED));
2376        }    else if (code == TransitSectionAction.PRESTARTDELAY) {
2377            includeCodes.add(TransitSectionAction.PRESTARTRESUME);
2378        } else {
2379            excludeCodes.add(TransitSectionAction.PRESTARTRESUME);
2380        }
2381        for (int i = 0; i <= TransitSectionAction.NUM_WHATS; i++) {
2382            if (excludeCodes.size() > 0) {
2383                if (! excludeCodes.contains(i)) {
2384                    whatBox.addItem(getWhatMenuText(i));
2385                }
2386            } else if (includeCodes.size() > 0) {
2387                if (includeCodes.contains(i)) {
2388                    whatBox.addItem(getWhatMenuText(i));
2389                }
2390            } else {
2391                whatBox.addItem(getWhatMenuText(i));
2392            }
2393        }
2394    }
2395
2396    private String getWhatMenuText(int i) {
2397        switch (i) {
2398            case TransitSectionAction.SELECTWHAT:
2399                return rbx.getString("SelectWhat");
2400            case TransitSectionAction.TERMINATETRAIN:
2401                return rbx.getString("TerminateTrain");
2402            case TransitSectionAction.LOADTRAININFO:
2403                return rbx.getString("LoadTrainInfo");
2404            case TransitSectionAction.PAUSE:
2405                return rbx.getString("Pause");
2406            case TransitSectionAction.SETMAXSPEED:
2407                return rbx.getString("SetMaxSpeed");
2408            case TransitSectionAction.SETCURRENTSPEED:
2409                return rbx.getString("SetTrainSpeed");
2410            case TransitSectionAction.RAMPTRAINSPEED:
2411                return rbx.getString("RampTrainSpeed");
2412            case TransitSectionAction.TOMANUALMODE:
2413                return rbx.getString("ToManualMode");
2414            case TransitSectionAction.SETLIGHT:
2415                return rbx.getString("SetLight");
2416            case TransitSectionAction.STARTBELL:
2417                return rbx.getString("StartBell");
2418            case TransitSectionAction.STOPBELL:
2419                return rbx.getString("StopBell");
2420            case TransitSectionAction.SOUNDHORN:
2421                return rbx.getString("SoundHorn");
2422            case TransitSectionAction.SOUNDHORNPATTERN:
2423                return rbx.getString("SoundHornPattern");
2424            case TransitSectionAction.LOCOFUNCTION:
2425                return rbx.getString("LocoFunction");
2426            case TransitSectionAction.SETSENSORACTIVE:
2427                return rbx.getString("SetSensorActive");
2428            case TransitSectionAction.SETSENSORINACTIVE:
2429                return rbx.getString("SetSensorInactive");
2430            case TransitSectionAction.HOLDSIGNAL:
2431                return rbx.getString("HoldSignal");
2432            case TransitSectionAction.RELEASESIGNAL:
2433                return rbx.getString("ReleaseSignal");
2434            case TransitSectionAction.ESTOP:
2435                return rbx.getString("EStop");
2436            case TransitSectionAction.PRESTARTRESUME:
2437                return rbx.getString("PreStartResume");
2438            default:
2439                log.warn("Unhandled transit section action code: {}", i);
2440                return rbx.getString("SelectWhat");
2441        }
2442    }
2443
2444    private int getWhatMenuCode(String s) {
2445        if (s.equals(rbx.getString("SelectWhat"))) {
2446            return TransitSectionAction.SELECTWHAT;
2447        }
2448        if (s.equals(rbx.getString("TerminateTrain"))) {
2449            return TransitSectionAction.TERMINATETRAIN;
2450        }
2451        if (s.equals(rbx.getString("LoadTrainInfo"))) {
2452            return TransitSectionAction.LOADTRAININFO;
2453        }
2454        if (s.equals(rbx.getString("Pause"))) {
2455            return TransitSectionAction.PAUSE;
2456        }
2457        if (s.equals(rbx.getString("SetMaxSpeed"))) {
2458            return TransitSectionAction.SETMAXSPEED;
2459        }
2460        if (s.equals(rbx.getString("SetTrainSpeed"))) {
2461            return TransitSectionAction.SETCURRENTSPEED;
2462        }
2463        if (s.equals(rbx.getString("RampTrainSpeed"))) {
2464            return TransitSectionAction.RAMPTRAINSPEED;
2465        }
2466        if (s.equals(rbx.getString("ToManualMode"))) {
2467            return TransitSectionAction.TOMANUALMODE;
2468        }
2469        if (s.equals(rbx.getString("SetLight"))) {
2470            return TransitSectionAction.SETLIGHT;
2471        }
2472        if (s.equals(rbx.getString("StartBell"))) {
2473            return TransitSectionAction.STARTBELL;
2474        }
2475        if (s.equals(rbx.getString("StopBell"))) {
2476            return TransitSectionAction.STOPBELL;
2477        }
2478        if (s.equals(rbx.getString("SoundHorn"))) {
2479            return TransitSectionAction.SOUNDHORN;
2480        }
2481        if (s.equals(rbx.getString("SoundHornPattern"))) {
2482            return TransitSectionAction.SOUNDHORNPATTERN;
2483        }
2484        if (s.equals(rbx.getString("LocoFunction"))) {
2485            return TransitSectionAction.LOCOFUNCTION;
2486        }
2487        if (s.equals(rbx.getString("SetSensorActive"))) {
2488            return TransitSectionAction.SETSENSORACTIVE;
2489        }
2490        if (s.equals(rbx.getString("SetSensorInactive"))) {
2491            return TransitSectionAction.SETSENSORINACTIVE;
2492        }
2493        if (s.equals(rbx.getString("HoldSignal"))) {
2494            return TransitSectionAction.HOLDSIGNAL;
2495        }
2496        if (s.equals(rbx.getString("ReleaseSignal"))) {
2497            return TransitSectionAction.RELEASESIGNAL;
2498        }
2499        if (s.equals(rbx.getString("EStop"))) {
2500            return TransitSectionAction.ESTOP;
2501        }
2502        if (s.equals(rbx.getString("PreStartResume"))) {
2503            return TransitSectionAction.PRESTARTRESUME;
2504        }
2505        log.warn("Unhandled transit section action text: {}", s);
2506        return 0;
2507    }
2508
2509    private void initializeBlockBox() {
2510        blockList = sectionList.get(activeRow).getBlockList();
2511        blockBox.removeAllItems();
2512        for (int i = 0; i < blockList.size(); i++) {
2513            String s = blockList.get(i).getDisplayName();
2514            blockBox.addItem(s);
2515        }
2516    }
2517
2518    private void setBlockBox() {
2519        if (editActionMode) {
2520            if ((curTSA.getWhenCode() == TransitSectionAction.BLOCKENTRY)
2521                    || (curTSA.getWhenCode() == TransitSectionAction.BLOCKEXIT)) {
2522                // assumes that initializeBlockBox has been called prior to this call
2523                for (int i = 0; i < blockList.size(); i++) {
2524                    if (curTSA.getStringWhen().equals(blockList.get(i).getSystemName())) {
2525                        blockBox.setSelectedIndex(i);
2526                        return;
2527                    }
2528                }
2529            }
2530        }
2531        blockBox.setSelectedIndex(0);
2532    }
2533
2534    private void editAction(int r) {
2535        curTSA = action.get(activeRow).get(r);
2536        editActionMode = true;
2537        addEditActionWindow();
2538    }
2539
2540    private void deleteAction(int r) {
2541        TransitSectionAction tsa = action.get(activeRow).get(r);
2542        action.get(activeRow).remove(r);
2543        tsa.dispose();
2544        actionTableModel.fireTableDataChanged();
2545    }
2546
2547    /**
2548     * Build display When string for Actions table.
2549     *
2550     * @param r row in the Special Actions table. A TransitSectionAction must be
2551     *          available for this row.
2552     * @return display string including entered values
2553     */
2554    private String getWhenText(int r) {
2555        TransitSectionAction tsa = action.get(activeRow).get(r);
2556        switch (tsa.getWhenCode()) {
2557            case TransitSectionAction.ENTRY:
2558                if (tsa.getDataWhen() > 0) {
2559                    return java.text.MessageFormat.format(rbx.getString("OnEntryDelayedFull"),
2560                            new Object[]{"" + tsa.getDataWhen()});
2561                }
2562                return rbx.getString("OnEntryFull");
2563            case TransitSectionAction.EXIT:
2564                if (tsa.getDataWhen() > 0) {
2565                    return java.text.MessageFormat.format(rbx.getString("OnExitDelayedFull"),
2566                            new Object[]{"" + tsa.getDataWhen()});
2567                }
2568                return rbx.getString("OnExitFull");
2569            case TransitSectionAction.BLOCKENTRY:
2570                if (tsa.getDataWhen() > 0) {
2571                    return java.text.MessageFormat.format(rbx.getString("OnBlockEntryDelayedFull"),
2572                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2573                }
2574                return java.text.MessageFormat.format(rbx.getString("OnBlockEntryFull"),
2575                        new Object[]{tsa.getStringWhen()});
2576            case TransitSectionAction.BLOCKEXIT:
2577                if (tsa.getDataWhen() > 0) {
2578                    return java.text.MessageFormat.format(rbx.getString("OnBlockExitDelayedFull"),
2579                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2580                }
2581                return java.text.MessageFormat.format(rbx.getString("OnBlockExitFull"),
2582                        new Object[]{tsa.getStringWhen()});
2583            case TransitSectionAction.TRAINSTOP:
2584                if (tsa.getDataWhen() > 0) {
2585                    return java.text.MessageFormat.format(rbx.getString("TrainStopDelayedFull"),
2586                            new Object[]{"" + tsa.getDataWhen()});
2587                }
2588                return rbx.getString("TrainStopFull");
2589            case TransitSectionAction.TRAINSTART:
2590                if (tsa.getDataWhen() > 0) {
2591                    return java.text.MessageFormat.format(rbx.getString("TrainStartDelayedFull"),
2592                            new Object[]{"" + tsa.getDataWhen()});
2593                }
2594                return rbx.getString("TrainStartFull");
2595            case TransitSectionAction.SENSORACTIVE:
2596                if (tsa.getDataWhen() > 0) {
2597                    return java.text.MessageFormat.format(rbx.getString("OnSensorActiveDelayedFull"),
2598                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2599                }
2600                return java.text.MessageFormat.format(rbx.getString("OnSensorActiveFull"),
2601                        new Object[]{tsa.getStringWhen()});
2602            case TransitSectionAction.SENSORINACTIVE:
2603                if (tsa.getDataWhen() > 0) {
2604                    return java.text.MessageFormat.format(rbx.getString("OnSensorInactiveDelayedFull"),
2605                            new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2606                }
2607                return java.text.MessageFormat.format(rbx.getString("OnSensorInactiveFull"),
2608                        new Object[]{tsa.getStringWhen()});
2609            case TransitSectionAction.PRESTARTDELAY:
2610                return java.text.MessageFormat.format(rbx.getString("PreStartDelayFull"),
2611                        new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2612            case TransitSectionAction.PRESTARTACTION:
2613                return java.text.MessageFormat.format(rbx.getString("PreStartActionFull"),
2614                        new Object[]{"" + tsa.getDataWhen(), tsa.getStringWhen()});
2615            default:
2616                log.warn("Unhandled transit section action When code: {}",tsa.getWhenCode());
2617                return("");
2618        }
2619    }
2620
2621    /**
2622     * Build display What string for Actions table.
2623     *
2624     * @param r row in the Special Actions table. A TransitSectionAction must be
2625     *          available for this row.
2626     * @return display string including entered values
2627     */
2628    private String getWhatText(int r) {
2629        TransitSectionAction tsa = action.get(activeRow).get(r);
2630        switch (tsa.getWhatCode()) {
2631            case TransitSectionAction.PAUSE:
2632                return java.text.MessageFormat.format(rbx.getString("PauseFull"),
2633                        new Object[]{tsa.getDataWhat1()});
2634            case TransitSectionAction.SETMAXSPEED:
2635                return java.text.MessageFormat.format(rbx.getString("SetMaxSpeedFull"),
2636                        new Object[]{tsa.getDataWhat1()});
2637            case TransitSectionAction.SETCURRENTSPEED:
2638                return java.text.MessageFormat.format(rbx.getString("SetTrainSpeedFull"),
2639                        new Object[]{tsa.getDataWhat1()});
2640            case TransitSectionAction.RAMPTRAINSPEED:
2641                return java.text.MessageFormat.format(rbx.getString("RampTrainSpeedFull"),
2642                        new Object[]{"" + tsa.getDataWhat1()});
2643            case TransitSectionAction.TOMANUALMODE:
2644                if (tsa.getStringWhat().length() > 0) {
2645                    return java.text.MessageFormat.format(rbx.getString("ToManualModeAltFull"),
2646                            new Object[]{tsa.getStringWhat()});
2647                }
2648                return rbx.getString("ToManualModeFull");
2649            case TransitSectionAction.SETLIGHT:
2650                if (tsa.getStringWhat().equals("Off")) {
2651                    return java.text.MessageFormat.format(rbx.getString("SetLightFull"),
2652                        new Object[]{Bundle.getMessage("StateOff")});
2653                }
2654                return java.text.MessageFormat.format(rbx.getString("SetLightFull"),
2655                        new Object[]{Bundle.getMessage("StateOn")});
2656            case TransitSectionAction.STARTBELL:
2657                return rbx.getString("StartBellFull");
2658            case TransitSectionAction.STOPBELL:
2659                return rbx.getString("StopBellFull");
2660            case TransitSectionAction.SOUNDHORN:
2661                return java.text.MessageFormat.format(rbx.getString("SoundHornFull"),
2662                        new Object[]{tsa.getDataWhat1()});
2663            case TransitSectionAction.SOUNDHORNPATTERN:
2664                return java.text.MessageFormat.format(rbx.getString("SoundHornPatternFull"),
2665                        new Object[]{tsa.getStringWhat(), "" + tsa.getDataWhat1(), "" + tsa.getDataWhat2()});
2666            case TransitSectionAction.LOCOFUNCTION:
2667                if (tsa.getStringWhat().equals("Off")) {
2668                    return java.text.MessageFormat.format(rbx.getString("LocoFunctionFull"),
2669                            new Object[]{"" + tsa.getDataWhat1(), Bundle.getMessage("StateOff")});
2670                }
2671                return java.text.MessageFormat.format(rbx.getString("LocoFunctionFull"),
2672                        new Object[]{"" + tsa.getDataWhat1(), Bundle.getMessage("StateOn")});
2673            case TransitSectionAction.SETSENSORACTIVE:
2674                return java.text.MessageFormat.format(rbx.getString("SetSensorActiveFull"),
2675                        new Object[]{tsa.getStringWhat()});
2676            case TransitSectionAction.SETSENSORINACTIVE:
2677                return java.text.MessageFormat.format(rbx.getString("SetSensorInactiveFull"),
2678                        new Object[]{tsa.getStringWhat()});
2679            case TransitSectionAction.HOLDSIGNAL:
2680                return java.text.MessageFormat.format(rbx.getString("HoldSignalFull"),
2681                        new Object[]{tsa.getStringWhat()});
2682            case TransitSectionAction.RELEASESIGNAL:
2683                return java.text.MessageFormat.format(rbx.getString("ReleaseSignalFull"),
2684                        new Object[]{tsa.getStringWhat()});
2685            case TransitSectionAction.PRESTARTRESUME:
2686                return java.text.MessageFormat.format(rbx.getString("PreStartResumeFull"),
2687                        new Object[]{tsa.getDataWhen()});
2688            case TransitSectionAction.ESTOP:
2689                return rbx.getString("EStopFull");
2690            case TransitSectionAction.TERMINATETRAIN:
2691                return rbx.getString("TerminateTrain");
2692            case TransitSectionAction.LOADTRAININFO:
2693                switch (tWhatData2) {
2694                    case TransitSectionAction.LOCOADDRESSTYPEROSTER:
2695                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoRosterFull"),
2696                                new Object[]{tsa.getStringWhat(),tsa.getStringWhat2()});
2697                    case TransitSectionAction.LOCOADDRESSTYPENUMBER:
2698                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoNumberFull"),
2699                                new Object[]{tsa.getStringWhat(),tsa.getStringWhat2()});
2700                    case TransitSectionAction.LOCOADDRESSTYPECURRENT:
2701                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoCurrentFull"),
2702                                new Object[]{tsa.getStringWhat()});
2703                    case TransitSectionAction.LOCOADDRESSTYPEDEFAULT:
2704                    default:
2705                        return java.text.MessageFormat.format(rbx.getString("LoadTrainInfoDefaultFull"),
2706                                new Object[]{tsa.getStringWhat()});
2707                 }
2708            case TransitSectionAction.SELECTWHAT:
2709                return rbx.getString("SelectWhat");
2710            default:
2711                log.warn("Unhandled transit section action What code: {}", tsa.getWhatCode());
2712                break;
2713        }
2714        return "WHAT";
2715    }
2716
2717    private String getSectionNameByRow(int r) {
2718        return sectionList.get(r).getDisplayName();
2719    }
2720
2721    /**
2722     * Table model for Sections in Create/Edit Transit window.
2723     */
2724    public class SectionTableModel extends javax.swing.table.AbstractTableModel implements
2725            java.beans.PropertyChangeListener {
2726
2727        public static final int SEQUENCE_COLUMN = 0;
2728        public static final int SECTIONNAME_COLUMN = 1;
2729        public static final int ACTION_COLUMN = 2;
2730        public static final int SEC_DIRECTION_COLUMN = 3;
2731        public static final int ALTERNATE_COLUMN = 4;
2732        public static final int SAFE_COLUMN = 5;
2733        public static final int STOPALLOCATING_SENSOR = 6;
2734        public static final int NUMBER_OF_COLUMNS = 7;
2735
2736        public SectionTableModel() {
2737            super();
2738            addPcl();
2739        }
2740
2741        final void addPcl(){
2742            sectionManager.addPropertyChangeListener(this);
2743        }
2744
2745        @Override
2746        public void propertyChange(java.beans.PropertyChangeEvent e) {
2747            if (e.getPropertyName().equals("length")) { // NOI18N
2748                // a new NamedBean is available in the manager
2749                fireTableDataChanged();
2750            }
2751            if (e.getSource() instanceof SensorManager) {
2752                if (e.getPropertyName().equals("DisplayListName")) {
2753                    updateSensorList();
2754                }
2755            }
2756        }
2757
2758        @Override
2759        public Class<?> getColumnClass(int c) {
2760            switch (c) {
2761                case ACTION_COLUMN:
2762                    return JButton.class;
2763                case SAFE_COLUMN:
2764                    return Boolean.class;
2765                case STOPALLOCATING_SENSOR:
2766                    return JComboBox.class;
2767                default:
2768                    return super.getColumnClass(c);
2769            }
2770        }
2771
2772        @Override
2773        public int getColumnCount() {
2774            return NUMBER_OF_COLUMNS;
2775        }
2776
2777        @Override
2778        public int getRowCount() {
2779            return (sectionList.size());
2780        }
2781
2782        @Override
2783        public boolean isCellEditable(int r, int c) {
2784            switch (c) {
2785                case ACTION_COLUMN:
2786                case SAFE_COLUMN:
2787                case STOPALLOCATING_SENSOR:
2788                    return true;
2789                default:
2790                    return false;
2791            }
2792        }
2793
2794        @Override
2795        public String getColumnName(int col) {
2796            switch (col) {
2797                case SEQUENCE_COLUMN:
2798                    return rbx.getString("SequenceColName");
2799                case SECTIONNAME_COLUMN:
2800                    return Bundle.getMessage("BeanNameSection");
2801                case ACTION_COLUMN:
2802                    return rbx.getString("ActionColName");
2803                case SEC_DIRECTION_COLUMN:
2804                    return rbx.getString("DirectionColName");
2805                case ALTERNATE_COLUMN:
2806                    return rbx.getString("AlternateColName");
2807                case SAFE_COLUMN:
2808                    return rbx.getString("SafeColName");
2809                case STOPALLOCATING_SENSOR:
2810                    return rbx.getString("StopAllocationColName");
2811               default:
2812                    return "";
2813            }
2814        }
2815
2816        @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
2817                                justification="better to keep cases in column order rather than to combine")
2818        public int getPreferredWidth(int col) {
2819            switch (col) {
2820                case SEQUENCE_COLUMN:
2821                    return new JTextField(8).getPreferredSize().width;
2822                case SECTIONNAME_COLUMN:
2823                    return new JTextField(17).getPreferredSize().width;
2824                case ACTION_COLUMN:
2825                    return new JTextField(12).getPreferredSize().width;
2826                case SEC_DIRECTION_COLUMN:
2827                    return new JTextField(12).getPreferredSize().width;
2828                case ALTERNATE_COLUMN:
2829                    return new JTextField(12).getPreferredSize().width;
2830                case SAFE_COLUMN:
2831                    return new JTextField(4).getPreferredSize().width;
2832                case STOPALLOCATING_SENSOR:
2833                    return new JTextField(12).getPreferredSize().width;
2834                default:
2835                    // fall through
2836                    break;
2837            }
2838            return new JTextField(5).getPreferredSize().width;
2839        }
2840
2841        @Override
2842        public Object getValueAt(int r, int c) {
2843            int rx = r;
2844            if (rx > sectionList.size()) {
2845                return null;
2846            }
2847            switch (c) {
2848                case SEQUENCE_COLUMN:
2849                    return ("" + sequence.get(rx));
2850                case SECTIONNAME_COLUMN:
2851                    return (getSectionNameByRow(rx));
2852                case ACTION_COLUMN:
2853                    return rbx.getString("AddEditActions");
2854                case SEC_DIRECTION_COLUMN:
2855                    if (direction.get(rx) == Section.FORWARD) {
2856                        return rbx.getString("SectionForward");
2857                    } else if (direction.get(rx) == Section.REVERSE) {
2858                        return rbx.getString("SectionReverse");
2859                    }
2860                    return Bundle.getMessage("BeanStateUnknown");
2861                case ALTERNATE_COLUMN:
2862                    if (alternate.get(rx)) {
2863                        return rbx.getString("Alternate");
2864                    }
2865                    return rbx.getString("Primary");
2866                case SAFE_COLUMN:
2867                    return safe.get(rx);
2868                case STOPALLOCATING_SENSOR:
2869                    String sensor = sensorStopAllocation.get(rx);
2870                    JComboBox<String> cb = new JComboBox<>(sensorList);
2871                    JComboBoxUtil.setupComboBoxMaxRows(cb);
2872                    String name = (sensor != null) ? sensor : "";
2873                    cb.setSelectedItem(name);
2874                    return cb;
2875                default:
2876                    return Bundle.getMessage("BeanStateUnknown");
2877            }
2878        }
2879
2880        @Override
2881        public void setValueAt(Object value, int row, int col) {
2882            switch (col) {
2883                case ACTION_COLUMN:
2884                    addEditActionsPressed(row);
2885                    break;
2886                case SAFE_COLUMN:
2887                    boolean val = ((Boolean) value);
2888                    safe.set(row, val); // use checkbox to show Safe
2889                    break;
2890                case STOPALLOCATING_SENSOR:
2891                    JComboBox<?> cb = (JComboBox<?>) value;
2892                    if (cb.getSelectedIndex() < 0) {
2893                        sensorStopAllocation.set(row, "");
2894                    } else {
2895                        sensorStopAllocation.set(row, (String) cb.getSelectedItem());
2896                    }
2897                    break;
2898                default:
2899                    break;
2900            }
2901        }
2902
2903        public void dispose(){
2904            sectionManager.removePropertyChangeListener(this);
2905        }
2906    }
2907
2908    private void updateSensorList() {
2909        Set<Sensor> nameSet = InstanceManager.getDefault(SensorManager.class).getNamedBeanSet();
2910        String[] displayList = new String[nameSet.size()];
2911        int i = 0;
2912        for (Sensor nBean : nameSet) {
2913            if (nBean != null) {
2914                displayList[i++] = nBean.getDisplayName();
2915            }
2916        }
2917        java.util.Arrays.sort(displayList, new jmri.util.AlphanumComparator());
2918        sensorList = new String[displayList.length + 1];
2919        sensorList[0] = "";
2920        i = 1;
2921        for (String name : displayList) {
2922            sensorList[i] = name;
2923            i++;
2924        }
2925    }
2926
2927
2928    /**
2929     * Table model for Actions in Special Actions window. Currently shows max. 5
2930     * rows.
2931     */
2932    public class SpecialActionTableModel extends javax.swing.table.AbstractTableModel implements
2933            java.beans.PropertyChangeListener {
2934
2935        public static final int WHEN_COLUMN = 0;
2936        public static final int WHAT_COLUMN = 1;
2937        public static final int EDIT_COLUMN = 2;
2938        public static final int REMOVE_COLUMN = 3;
2939
2940        public SpecialActionTableModel() {
2941            super();
2942            addPcl();
2943        }
2944
2945        final void addPcl(){
2946            sectionManager.addPropertyChangeListener(this);
2947        }
2948
2949        @Override
2950        public void propertyChange(java.beans.PropertyChangeEvent e) {
2951            if (e.getPropertyName().equals("length")) { // NOI18N
2952                // a new NamedBean is available in the manager
2953                fireTableDataChanged();
2954            }
2955        }
2956
2957        @Override
2958        public Class<?> getColumnClass(int c) {
2959            switch (c) {
2960                case EDIT_COLUMN:
2961                case REMOVE_COLUMN:
2962                    return JButton.class;
2963                case WHEN_COLUMN:
2964                case WHAT_COLUMN:
2965                default:
2966                    return String.class;
2967            }
2968        }
2969
2970        @Override
2971        public int getColumnCount() {
2972            return REMOVE_COLUMN + 1;
2973        }
2974
2975        @Override
2976        public int getRowCount() {
2977            return (action.get(activeRow).size());
2978        }
2979
2980        @Override
2981        public boolean isCellEditable(int r, int c) {
2982            switch (c) {
2983                case EDIT_COLUMN:
2984                case REMOVE_COLUMN:
2985                    return true;
2986                default:
2987                    return false;
2988            }
2989        }
2990
2991        @Override
2992        public String getColumnName(int col) {
2993            switch (col) {
2994                case WHEN_COLUMN:
2995                    return rbx.getString("WhenColName");
2996                case WHAT_COLUMN:
2997                    return rbx.getString("WhatColName");
2998                default:
2999                    return "";
3000            }
3001        }
3002
3003        public int getPreferredWidth(int col) {
3004            switch (col) {
3005                case WHEN_COLUMN:
3006                case WHAT_COLUMN:
3007                    return new JTextField(50).getPreferredSize().width;
3008                case EDIT_COLUMN:
3009                case REMOVE_COLUMN:
3010                default:
3011                    return new JTextField(8).getPreferredSize().width;
3012            }
3013        }
3014
3015        @Override
3016        public Object getValueAt(int r, int c) {
3017            int rx = r;
3018            if (rx > action.get(activeRow).size()) {
3019                return null;
3020            }
3021            switch (c) {
3022                case WHEN_COLUMN:
3023                    return (getWhenText(rx));
3024                case WHAT_COLUMN:
3025                    return (getWhatText(rx));
3026                case EDIT_COLUMN:
3027                    return Bundle.getMessage("ButtonEdit");
3028                case REMOVE_COLUMN:
3029                    return Bundle.getMessage("ButtonDelete");
3030                default:
3031                    return Bundle.getMessage("BeanStateUnknown"); // normally not in use
3032            }
3033        }
3034
3035        @Override
3036        public void setValueAt(Object value, int row, int col) {
3037            if (col == EDIT_COLUMN) {
3038                // set up to edit
3039                editAction(row);
3040            }
3041            else if (col == REMOVE_COLUMN) {
3042                deleteAction(row);
3043            }
3044        }
3045
3046        public void dispose(){
3047            sectionManager.removePropertyChangeListener(this);
3048        }
3049    }
3050
3051    @Override
3052    protected String getClassName() {
3053        return TransitTableAction.class.getName();
3054    }
3055
3056    @Override
3057    public String getClassDescription() {
3058        return Bundle.getMessage("TitleTransitTable");
3059    }
3060
3061    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TransitTableAction.class);
3062
3063}