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