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