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