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