001package jmri.jmrit.entryexit;
002
003import java.awt.Color;
004import java.awt.Container;
005import java.awt.GridLayout;
006import java.awt.event.ActionEvent;
007import java.awt.event.ActionListener;
008import java.beans.PropertyChangeEvent;
009import java.beans.PropertyChangeListener;
010import java.util.ArrayList;
011import java.util.List;
012import java.util.SortedSet;
013import java.util.TreeSet;
014import javax.swing.*;
015import javax.swing.table.TableCellEditor;
016import javax.swing.table.TableColumn;
017import javax.swing.table.TableRowSorter;
018import jmri.InstanceManager;
019import jmri.NamedBean;
020import jmri.jmrit.display.EditorManager;
021import jmri.jmrit.display.layoutEditor.LayoutEditor;
022import jmri.jmrit.display.layoutEditor.LayoutSlip;
023import jmri.jmrit.display.layoutEditor.LayoutTurnout;
024import jmri.jmrit.display.layoutEditor.LevelXing;
025import jmri.jmrit.display.layoutEditor.PositionablePoint;
026import jmri.swing.NamedBeanComboBox;
027import jmri.util.JmriJFrame;
028import jmri.util.swing.*;
029import jmri.util.table.ButtonEditor;
030import jmri.util.table.ButtonRenderer;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034
035/**
036 * JPanel to create a new EntryExitPair.
037 *
038 * @author Bob Jacobsen Copyright (C) 2009
039 */
040public class AddEntryExitPairPanel extends jmri.util.swing.JmriPanel {
041
042    JComboBox<String> selectPanel = new JComboBox<>();
043    JComboBox<String> fromPoint = new JComboBox<>();
044    JComboBox<String> toPoint = new JComboBox<>();
045
046    String[] interlockTypes = {Bundle.getMessage("SetTurnoutsOnly"), Bundle.getMessage("SetTurnoutsAndSignalMasts"), Bundle.getMessage("FullInterlock")};  // NOI18N
047    JComboBox<String> typeBox = new JComboBox<>(interlockTypes);
048
049    List<LayoutEditor> panels;
050
051    EntryExitPairs nxPairs = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class);
052
053    // signalling.EntryExitBundle via Bundle method
054
055    public AddEntryExitPairPanel(LayoutEditor panel) {
056
057        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
058        // combo boxes selection panel
059        JPanel top = new JPanel();
060        top.setBorder(BorderFactory.createEtchedBorder());
061        top.setLayout(new GridLayout(4, 2));
062        top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("SelectPanel")), SwingConstants.RIGHT));  // NOI18N
063        top.add(selectPanel);
064        selectPanel.removeAllItems();
065        panels = InstanceManager.getDefault(EditorManager.class).getList(LayoutEditor.class);
066        for (int i = 0; i < panels.size(); i++) {
067            selectPanel.addItem(panels.get(i).getLayoutName());
068        }
069        if (panel != null) {
070            selectPanel.setSelectedItem(panel.getLayoutName());
071        }
072
073        top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("FromLocation")), SwingConstants.RIGHT));  // NOI18N
074        top.add(fromPoint);
075        ActionListener selectPanelListener = (ActionEvent e) -> {
076            selectPointsFromPanel();
077            nxModel.setPanel(panels.get(selectPanel.getSelectedIndex()));
078        };
079        selectPointsFromPanel();
080        selectPanel.addActionListener(selectPanelListener);
081
082        top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("ToLocation")), SwingConstants.RIGHT));  // NOI18N
083        top.add(toPoint);
084
085        JComboBoxUtil.setupComboBoxMaxRows(fromPoint);
086        JComboBoxUtil.setupComboBoxMaxRows(toPoint);
087
088        top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("NXType")), SwingConstants.RIGHT));  // NOI18N
089        top.add(typeBox);
090        add(top);
091
092        // button panel
093        JPanel p = new JPanel();
094        JButton ok = new JButton(Bundle.getMessage("AddPair"));  // NOI18N
095        p.add(ok);
096        ok.addActionListener((ActionEvent e) -> {
097            addButton();
098        });
099
100        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
101        JButton auto;
102        p.add(auto = new JButton(Bundle.getMessage("AutoGenerate")));  // NOI18N
103        auto.addActionListener((ActionEvent e) -> {
104            autoDiscovery();
105        });
106        p.add(auto);
107        add(p);
108        nxModel = new TableModel(panel);
109        nxDataTable = new JTable(nxModel);
110        nxDataTable.setRowSorter(new TableRowSorter<>(nxModel));
111        nxDataScroll = new JScrollPane(nxDataTable);
112        nxModel.configureTable(nxDataTable);
113        java.awt.Dimension dataTableSize = nxDataTable.getPreferredSize();
114        // width is right, but if table is empty, it's not high
115        // enough to reserve much space.
116        dataTableSize.height = Math.max(dataTableSize.height, 400);
117        nxDataScroll.getViewport().setPreferredSize(dataTableSize);
118        add(nxDataScroll);
119    }
120
121    LayoutEditor panel;
122
123    private void addButton() {
124        ValidPoints from = getValidPointFromCombo(fromPoint);
125        ValidPoints to = getValidPointFromCombo(toPoint);
126        if (from == null || to == null) {
127            return;
128        }
129
130        nxPairs.addNXDestination(from.getPoint(), to.getPoint(), panel);
131        nxPairs.setEntryExitType(from.getPoint(), panel, to.getPoint(), typeBox.getSelectedIndex());
132    }
133
134    jmri.util.JmriJFrame entryExitFrame = null;
135    JLabel sourceLabel = new JLabel();
136
137    private void autoDiscovery() {
138        if (!InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class).isAdvancedRoutingEnabled()) {
139            int response = JOptionPane.showConfirmDialog(null, Bundle.getMessage("EnableLayoutBlockRouting"));  // NOI18N
140            if (response == 0) {
141                InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class).enableAdvancedRouting(true);
142                JOptionPane.showMessageDialog(null, Bundle.getMessage("LayoutBlockRoutingEnabled"));  // NOI18N
143            }
144        }
145        entryExitFrame = new jmri.util.JmriJFrame(Bundle.getMessage("DiscoverEntryExitPairs"), false, false);   // NOI18N
146        entryExitFrame.setPreferredSize(null);
147        JPanel panel1 = new JPanel();
148        sourceLabel = new JLabel(Bundle.getMessage("DiscoveringEntryExitPairs"));  // NOI18N
149        /*ImageIcon i;
150         i = new ImageIcon(FileUtil.findURL("resources/icons/misc/gui3/process-working.gif"));
151         JLabel label = new JLabel();
152         label.setIcon(i);
153         panel1.add(label);*/
154        panel1.add(sourceLabel);
155
156        entryExitFrame.add(panel1);
157        entryExitFrame.pack();
158        entryExitFrame.setVisible(true);
159        int retval = JOptionPane.showOptionDialog(null, Bundle.getMessage("AutoGenEntryExitMessage"), Bundle.getMessage("AutoGenEntryExitTitle"),  // NOI18N
160                JOptionPane.YES_NO_OPTION,
161                JOptionPane.QUESTION_MESSAGE, null, null, null);
162        if (retval == 0) {
163            final PropertyChangeListener propertyNXListener = new PropertyChangeListener() {
164                @Override
165                public void propertyChange(PropertyChangeEvent evt) {
166                    if (evt.getPropertyName().equals("autoGenerateComplete")) {  // NOI18N
167                        if (entryExitFrame != null) {
168                            entryExitFrame.setVisible(false);
169                            entryExitFrame.dispose();
170                        }
171                        nxPairs.removePropertyChangeListener(this);
172                        JOptionPane.showMessageDialog(null, Bundle.getMessage("AutoGenComplete"));  // NOI18N
173                    }
174                }
175            };
176            try {
177                nxPairs.addPropertyChangeListener(propertyNXListener);
178                nxPairs.automaticallyDiscoverEntryExitPairs(panels.get(selectPanel.getSelectedIndex()), typeBox.getSelectedIndex());
179            } catch (jmri.JmriException e) {
180                nxPairs.removePropertyChangeListener(propertyNXListener);
181                JOptionPane.showMessageDialog(null, e.toString());
182                entryExitFrame.setVisible(false);
183            }
184        } else {
185            entryExitFrame.setVisible(false);
186        }
187    }
188
189    ValidPoints getValidPointFromCombo(JComboBox<String> box) {
190        String item = (String) box.getSelectedItem();
191        for (int i = 0; i < validPoints.size(); i++) {
192            if (validPoints.get(i).getDescription().equals(item)) {
193                return validPoints.get(i);
194            }
195        }
196        return null;
197    }
198
199    List<ValidPoints> validPoints = new ArrayList<>();
200    boolean doFromCombo;
201    SortedSet<String> fromSet = new TreeSet<>();
202    SortedSet<String> toSet = new TreeSet<>();
203
204    private void selectPointsFromPanel() {
205        if (selectPanel.getSelectedIndex() == -1) {
206            return;
207        }
208        if (panel == panels.get(selectPanel.getSelectedIndex())) {
209            return;
210        }
211        panel = panels.get(selectPanel.getSelectedIndex());
212        fromSet.clear();
213        toSet.clear();
214        doFromCombo = true;
215        selectPoints(panel);
216
217        // Do other panels if any
218        doFromCombo = false;
219        panels = InstanceManager.getDefault(EditorManager.class).getList(LayoutEditor.class);
220        for (int i = 0; i < panels.size(); i++) {
221            if (panels.get(i) != panel) {
222                selectPoints(panels.get(i));
223            }
224        }
225
226        // Update the combo boxes
227        fromPoint.removeAllItems();
228        fromSet.forEach((ent) -> {
229            fromPoint.addItem(ent);
230        });
231        toPoint.removeAllItems();
232        toSet.forEach((ent) -> {
233            toPoint.addItem(ent);
234        });
235    }
236
237    private void selectPoints(LayoutEditor panel) {
238        for (PositionablePoint pp : panel.getPositionablePoints()) {
239            addPointToCombo(pp.getWestBoundSignalMastName(), pp.getWestBoundSensorName());
240            addPointToCombo(pp.getEastBoundSignalMastName(), pp.getEastBoundSensorName());
241        }
242
243        for (LayoutTurnout t : panel.getLayoutTurnouts()) {
244            addPointToCombo(t.getSignalAMastName(), t.getSensorAName());
245            addPointToCombo(t.getSignalBMastName(), t.getSensorBName());
246            addPointToCombo(t.getSignalCMastName(), t.getSensorCName());
247            addPointToCombo(t.getSignalDMastName(), t.getSensorDName());
248        }
249
250        for (LevelXing xing : panel.getLevelXings()) {
251            addPointToCombo(xing.getSignalAMastName(), xing.getSensorAName());
252            addPointToCombo(xing.getSignalBMastName(), xing.getSensorBName());
253            addPointToCombo(xing.getSignalCMastName(), xing.getSensorCName());
254            addPointToCombo(xing.getSignalDMastName(), xing.getSensorDName());
255        }
256
257        for (LayoutSlip slip : panel.getLayoutSlips()) {
258            addPointToCombo(slip.getSignalAMastName(), slip.getSensorAName());
259            addPointToCombo(slip.getSignalBMastName(), slip.getSensorBName());
260            addPointToCombo(slip.getSignalCMastName(), slip.getSensorCName());
261            addPointToCombo(slip.getSignalDMastName(), slip.getSensorDName());
262        }
263    }
264
265    void addPointToCombo(String signalMastName, String sensorName) {
266        if (sensorName != null && !sensorName.isEmpty()) {
267            String description = sensorName;
268            NamedBean source = InstanceManager.sensorManagerInstance().getSensor(sensorName);
269            if (signalMastName != null && !signalMastName.isEmpty()) {
270                description = sensorName + " (" + signalMastName + ")";
271            }
272            validPoints.add(new ValidPoints(source, description));
273            if (doFromCombo) {
274                fromSet.add(description);
275            }
276            toSet.add(description);
277        }
278    }
279
280    JTable nxDataTable;
281    JScrollPane nxDataScroll;
282
283    TableModel nxModel;
284
285    static final int FROMPOINTCOL = 0;
286    static final int TOPOINTCOL = 1;
287    static final int ACTIVECOL = 2;
288    static final int CLEARCOL = 3;
289    static final int BOTHWAYCOL = 4;
290    static final int DELETECOL = 5;
291    static final int TYPECOL = 6;
292    static final int ENABLEDCOL = 7;
293
294    static final int NUMCOL = ENABLEDCOL + 1;
295
296    class TableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener {
297
298        //needs a method to for when panel changes
299        //need a method to delete an item
300        //Possibly also to set a route.
301        // Since 4.17.4 has added functionality a property change listener to catch when paths go active.
302        TableModel(LayoutEditor panel) {
303            setPanel(panel);
304            nxPairs.addPropertyChangeListener(this);
305            source = nxPairs.getNxSource(panel);
306            dest = nxPairs.getNxDestination();
307        }
308
309        void setPanel(LayoutEditor panel) {
310            if (this.panel == panel) {
311                return;
312            }
313            this.panel = panel;
314            rowCount = nxPairs.getNxPairNumbers(panel);
315            updateNameList();
316            fireTableDataChanged();
317        }
318
319        LayoutEditor panel;
320
321        List<Object> source = null;
322        List<Object> dest = null;
323
324        void updateNameList() {
325            source = nxPairs.getNxSource(panel);
326            dest = nxPairs.getNxDestination();
327        }
328
329        int rowCount = 0;
330
331        @Override
332        public int getRowCount() {
333            return rowCount;
334        }
335
336        public void configureTable(JTable table) {
337            // allow reordering of the columns
338            table.getTableHeader().setReorderingAllowed(true);
339
340            // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541)
341            table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
342
343            // resize columns as requested
344            for (int i = 0; i < table.getColumnCount(); i++) {
345                int width = getPreferredWidth(i);
346                table.getColumnModel().getColumn(i).setPreferredWidth(width);
347            }
348            table.sizeColumnsToFit(-1);
349
350            configDeleteColumn(table);
351
352        }
353
354        @Override
355        public Object getValueAt(int row, int col) {
356            // get roster entry for row
357            if (panel == null) {
358                log.debug("no panel selected!");  // NOI18N
359                return Bundle.getMessage("ErrorTitle");  // NOI18N
360            }
361            switch (col) {
362                case FROMPOINTCOL:
363                    return nxPairs.getPointAsString((NamedBean) source.get(row), panel);
364                case TOPOINTCOL:
365                    return nxPairs.getPointAsString((NamedBean) dest.get(row), panel);
366                case ACTIVECOL:
367                    return isPairActive(row);
368                case BOTHWAYCOL:
369                    return !nxPairs.isUniDirection(source.get(row), panel, dest.get(row));
370                case ENABLEDCOL:
371                    return !nxPairs.isEnabled(source.get(row), panel, dest.get(row));
372                case CLEARCOL:
373                    return Bundle.getMessage("ButtonClear");  // NOI18N
374                case DELETECOL:
375                    return Bundle.getMessage("ButtonDelete");  // NOI18N
376                case TYPECOL:
377                    return NXTYPE_NAMES[nxPairs.getEntryExitType(source.get(row), panel, dest.get(row))];
378                default:
379                    return "";
380            }
381        }
382
383        @Override
384        public void setValueAt(Object value, int row, int col) {
385            if (col == DELETECOL) {
386                // button fired, delete Bean
387                deleteEntryExit(row, col);
388            }
389            if (col == CLEARCOL) {
390                nxPairs.cancelInterlock(source.get(row), panel, dest.get(row));
391            }
392            if (col == BOTHWAYCOL) {
393                boolean b = !((Boolean) value);
394                nxPairs.setUniDirection(source.get(row), panel, dest.get(row), b);
395            }
396            if (col == ENABLEDCOL) {
397                boolean b = !((Boolean) value);
398                nxPairs.setEnabled(source.get(row), panel, dest.get(row), b);
399            }
400            if (col == TYPECOL) {
401                String val = (String) value;
402                if (val.equals(Bundle.getMessage("SetTurnoutsOnly"))) { // I18N matching needs if-else  // NOI18N
403                    nxPairs.setEntryExitType(source.get(row), panel, dest.get(row), 0x00);
404                } else if (val.equals(Bundle.getMessage("SetTurnoutsAndSignalMasts"))) {  // NOI18N
405                    nxPairs.setEntryExitType(source.get(row), panel, dest.get(row), 0x01);
406                } else if (val.equals(Bundle.getMessage("FullInterlock"))) {
407                    nxPairs.setEntryExitType(source.get(row), panel, dest.get(row), 0x02);  // NOI18N
408                }
409            }
410        }
411
412        public int getPreferredWidth(int col) {
413            switch (col) {
414                case FROMPOINTCOL:
415                case TOPOINTCOL:
416                    return new JTextField(15).getPreferredSize().width;
417                case ACTIVECOL:
418                case BOTHWAYCOL:
419                case ENABLEDCOL:
420                    return new JTextField(5).getPreferredSize().width;
421                case CLEARCOL:
422                case DELETECOL:  //
423                    return new JTextField(22).getPreferredSize().width;
424                case TYPECOL:
425                    return new JTextField(10).getPreferredSize().width;
426                default:
427                    log.warn("Unexpected column in getPreferredWidth: {}", col);  // NOI18N
428                    return new JTextField(8).getPreferredSize().width;
429            }
430        }
431
432        protected void deleteEntryExit(int row, int col) {
433            NamedBean nbSource = ((NamedBean) source.get(row));
434            NamedBean nbDest = (NamedBean) dest.get(row);
435            nxPairs.deleteNxPair(nbSource, nbDest, panel);
436        }
437
438        String isPairActive(int row) {
439            if (nxPairs.isPathActive(source.get(row), dest.get(row), panel)) {
440                return (Bundle.getMessage("ButtonYes")); // "Yes"  // NOI18N
441            }
442            return ("");
443        }
444
445        @Override
446        public String getColumnName(int col) {
447            switch (col) {
448                case FROMPOINTCOL:
449                    return Bundle.getMessage("ColumnFrom");  // NOI18N
450                case TOPOINTCOL:
451                    return Bundle.getMessage("ColumnTo");  // NOI18N
452                case ACTIVECOL:
453                    return Bundle.getMessage("SensorStateActive"); // "Active"  // NOI18N
454                case DELETECOL:
455                    return "";
456                case CLEARCOL:
457                    return "";
458                case BOTHWAYCOL:
459                    return Bundle.getMessage("ColumnBoth");  // NOI18N
460                case TYPECOL:
461                    return Bundle.getMessage("NXType");  // NOI18N
462                case ENABLEDCOL:
463                    return Bundle.getMessage("Disabled");  // NOI18N
464                default:
465                    return "<UNKNOWN>";  // NOI18N
466            }
467        }
468
469        @Override
470        public Class<?> getColumnClass(int col) {
471            switch (col) {
472                case FROMPOINTCOL:
473                case TOPOINTCOL:
474                case ACTIVECOL:
475                    return String.class;
476                case DELETECOL:
477                case CLEARCOL:
478                    return JButton.class;
479                case BOTHWAYCOL:
480                case ENABLEDCOL:
481                    return Boolean.class;
482                case TYPECOL:
483                    return String.class;
484                default:
485                    return null;
486            }
487        }
488
489        @Override
490        public boolean isCellEditable(int row, int col) {
491            switch (col) {
492                case BOTHWAYCOL:
493                    Object obj = nxPairs.getEndPointLocation((NamedBean) dest.get(row), panel);
494                    if (obj instanceof PositionablePoint) {
495                        PositionablePoint point = (PositionablePoint) obj;
496                        if (point.getType() == PositionablePoint.PointType.END_BUMPER) {
497                            JOptionPane.showMessageDialog(null, Bundle.getMessage("EndBumperPoint"));  // NOI18N
498                            return false;
499                        }
500                    }
501                    if (!nxPairs.canBeBiDirectional(source.get(row), panel, dest.get(row))) {
502                        JOptionPane.showMessageDialog(null, Bundle.getMessage("BothWayTurnoutOnly"));  // NOI18N
503                        return false;
504                    }
505                    /*if(nxPairs.getEntryExitType(source.get(row), panel, dest.get(row))!=0x00){
506                     JOptionPane.showMessageDialog(null, Bundle.getMessage("BothWayTurnoutOnly"));
507                     return false;
508                     }*/
509                    return true;
510                case DELETECOL:
511                case CLEARCOL:
512                case ENABLEDCOL:
513                case TYPECOL:
514                    return true;
515                default:
516                    return false;
517            }
518        }
519
520        @Override
521        public int getColumnCount() {
522            return NUMCOL;
523        }
524
525        @Override
526        public void propertyChange(java.beans.PropertyChangeEvent e) {
527            if (e.getPropertyName().equals("length") || e.getPropertyName().equals("active")) {  // NOI18N
528                rowCount = nxPairs.getNxPairNumbers(panel);
529                updateNameList();
530                fireTableDataChanged();
531            }
532        }
533    }
534
535    String[] NXTYPE_NAMES = {Bundle.getMessage("SetTurnoutsOnly"), Bundle.getMessage("SetTurnoutsAndSignalMasts"), Bundle.getMessage("FullInterlock")};  // NOI18N
536    // Picked up in setValueAt() to read back from table
537
538    protected void configDeleteColumn(JTable table) {
539        // have the delete column hold a button
540        setColumnToHoldButton(table, DELETECOL,
541                new JButton(Bundle.getMessage("ButtonDelete")));  // NOI18N
542
543        setColumnToHoldButton(table, CLEARCOL,
544                new JButton(Bundle.getMessage("ButtonClear")));  // NOI18N
545
546        JComboBox<String> typeCombo = new JComboBox<>(NXTYPE_NAMES);
547
548        TableColumn col = table.getColumnModel().getColumn(TYPECOL);
549        col.setCellEditor(new DefaultCellEditor(typeCombo));
550    }
551
552    /**
553     * Service method to set up a column so that it will hold a button for its
554     * values.
555     *
556     * @param table  the table
557     * @param column the column
558     * @param sample Typical button, used for size
559     */
560    protected void setColumnToHoldButton(JTable table, int column, JButton sample) {
561        //TableColumnModel tcm = table.getColumnModel();
562        // install a button renderer & editor
563        ButtonRenderer buttonRenderer = new ButtonRenderer();
564        table.setDefaultRenderer(JButton.class, buttonRenderer);
565        TableCellEditor buttonEditor = new ButtonEditor(new JButton());
566        table.setDefaultEditor(JButton.class, buttonEditor);
567        // ensure the table rows, columns have enough room for buttons
568        table.setRowHeight(sample.getPreferredSize().height);
569        table.getColumnModel().getColumn(column)
570                .setPreferredWidth((sample.getPreferredSize().width) + 4);
571    }
572
573    static class ValidPoints {
574
575        NamedBean bean;
576        String description;
577
578        ValidPoints(NamedBean bean, String description) {
579            this.bean = bean;
580            this.description = description;
581        }
582
583        NamedBean getPoint() {
584            return bean;
585        }
586
587        String getDescription() {
588            return description;
589        }
590    }
591
592    // Variables for the Options menu item
593    JmriJFrame optionsFrame = null;
594    Container optionsPane = null;
595
596    String[] clearOptions = {
597            Bundle.getMessage("PromptUser"),   // NOI18N
598            Bundle.getMessage("ClearRoute"),   // NOI18N
599            Bundle.getMessage("CancelRoute"),  // NOI18N
600            Bundle.getMessage("StackRoute")};  // NOI18N
601    JComboBox<String> clearEntry = new JComboBox<>(clearOptions);
602
603    String[] overlapOptions = {
604            Bundle.getMessage("PromptUser"),   // NOI18N
605            Bundle.getMessage("CancelRoute"),  // NOI18N
606            Bundle.getMessage("StackRoute")};  // NOI18N
607    JComboBox<String> overlapEntry = new JComboBox<>(overlapOptions);
608
609    NamedBeanComboBox<jmri.Memory> memoryComboBox = new NamedBeanComboBox<>(
610            InstanceManager.getDefault(jmri.MemoryManager.class));
611    JSpinner memoryClearDelay = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1));
612
613    JTextField durationSetting = new JTextField(10);
614    String[] colorText = {"ColorClear", "Black", "DarkGray", "Gray",  // NOI18N
615        "LightGray", "White", "Red", "Pink", "Orange",  // NOI18N
616        "Yellow", "Green", "Blue", "Magenta", "Cyan"}; // NOI18N
617    Color[] colorCode = {null, Color.black, Color.darkGray, Color.gray,
618        Color.lightGray, Color.white, Color.red, Color.pink, Color.orange,
619        Color.yellow, Color.green, Color.blue, Color.magenta, Color.cyan};
620    int numColors = 14;  // number of entries in the above arrays
621    JCheckBox dispatcherUse = new JCheckBox(Bundle.getMessage("DispatcherInt"));  // NOI18N
622
623    JComboBox<String> settingTrackColorBox = new JComboBox<>();
624
625    private void initializeColorCombo(JComboBox<String> colorCombo) {
626        colorCombo.removeAllItems();
627        for (int i = 0; i < numColors; i++) {
628            colorCombo.addItem(Bundle.getMessage(colorText[i])); // I18N using Bundle.getMessage from higher level color list
629        }
630    }
631
632    private void setColorCombo(JComboBox<String> colorCombo, Color color) {
633        for (int i = 0; i < numColors; i++) {
634            if (color == colorCode[i]) {
635                colorCombo.setSelectedIndex(i);
636                return;
637            }
638        }
639    }
640
641    private Color getSelectedColor(JComboBox<String> colorCombo) {
642        return (colorCode[colorCombo.getSelectedIndex()]);
643    }
644
645    /**
646     * Build the Options window
647     * @param e the action event
648     */
649    protected void optionWindow(ActionEvent e) {
650        if (optionsFrame == null) {
651            optionsFrame = new JmriJFrame(Bundle.getMessage("OptionsTitle"), false, true);  // NOI18N
652            //optionsFrame.addHelpMenu("package.jmri.jmrit.dispatcher.Options", true);
653            optionsPane = optionsFrame.getContentPane();
654            optionsPane.setLayout(new BoxLayout(optionsFrame.getContentPane(), BoxLayout.Y_AXIS));
655
656            clearEntry.setSelectedIndex(nxPairs.getClearDownOption());
657            JPanel p1 = new JPanel();
658            //clearEntry.addActionListener(clearEntryListener);
659            clearEntry.setToolTipText(Bundle.getMessage("ReselectionTip"));  // NOI18N
660            p1.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("Reselection"))));  // NOI18N
661            p1.add(clearEntry);
662            optionsPane.add(p1);
663
664            overlapEntry.setSelectedIndex(nxPairs.getOverlapOption());
665            JPanel p1a = new JPanel();
666            overlapEntry.setToolTipText(Bundle.getMessage("OverlapSelectedTip"));  // NOI18N
667            p1a.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("OverlapSelected"))));  // NOI18N
668            p1a.add(overlapEntry);
669            optionsPane.add(p1a);
670
671            LayoutEditor.setupComboBox(memoryComboBox, false, true, false);
672            memoryComboBox.setSelectedItemByName(nxPairs.getMemoryOption());
673            JPanel p1b = new JPanel();
674            memoryComboBox.setToolTipText(Bundle.getMessage("MemoryOptionTip"));  // NOI18N
675            p1b.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("MemoryOption"))));  // NOI18N
676            p1b.add(memoryComboBox);
677            optionsPane.add(p1b);
678
679            memoryClearDelay.setValue(nxPairs.getMemoryClearDelay());
680            JPanel p1c = new JPanel();
681            memoryClearDelay.setToolTipText(Bundle.getMessage("MemoryClearTip"));  // NOI18N
682            p1c.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("MemoryClear"))));  // NOI18N
683            p1c.add(memoryClearDelay);
684            optionsPane.add(p1c);
685
686            JPanel p2 = new JPanel();
687            initializeColorCombo(settingTrackColorBox);
688            setColorCombo(settingTrackColorBox, nxPairs.getSettingRouteColor());
689            ActionListener settingTrackColorListener = (ActionEvent e1) -> {
690                if (getSelectedColor(settingTrackColorBox) != null) {
691                    durationSetting.setEnabled(true);
692                } else {
693                    durationSetting.setEnabled(false);
694                }
695            };
696
697            settingTrackColorBox.addActionListener(settingTrackColorListener);
698            p2.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("RouteSetColour"))));  // NOI18N
699            p2.add(settingTrackColorBox);
700            optionsPane.add(p2);
701            durationSetting.setText("" + nxPairs.getSettingTimer());
702            if (nxPairs.useDifferentColorWhenSetting()) {
703                durationSetting.setEnabled(true);
704            } else {
705                durationSetting.setEnabled(false);
706            }
707            JPanel p3 = new JPanel();
708            p3.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("SettingDuration"))));  // NOI18N
709            p3.add(durationSetting);
710            optionsPane.add(p3);
711
712            JPanel p4 = new JPanel();
713            p4.add(dispatcherUse);
714            dispatcherUse.setSelected(nxPairs.getDispatcherIntegration());
715            optionsPane.add(p4);
716
717            JButton ok = new JButton(Bundle.getMessage("ButtonOK"));  // NOI18N
718            optionsPane.add(ok);
719            ok.addActionListener((ActionEvent e1) -> {
720                optionSaveButton();
721            });
722        }
723        optionsFrame.pack();
724        optionsFrame.setVisible(true);
725    }
726
727    /**
728     * Save the option updates
729     */
730    void optionSaveButton() {
731        int settingTimer = 2000; // in milliseconds
732        try {
733            settingTimer = Integer.parseInt(durationSetting.getText());
734        } catch (Exception e) {
735            JOptionPane.showMessageDialog(null, Bundle.getMessage("ValueBeNumber"));  // NOI18N
736            return;
737        }
738        nxPairs.setSettingTimer(settingTimer);
739        nxPairs.setSettingRouteColor(getSelectedColor(settingTrackColorBox));
740        nxPairs.setClearDownOption(clearEntry.getSelectedIndex());
741        nxPairs.setOverlapOption(overlapEntry.getSelectedIndex());
742        nxPairs.setMemoryClearDelay((int) memoryClearDelay.getValue());
743        nxPairs.setDispatcherIntegration(dispatcherUse.isSelected());
744
745        String memoryName = memoryComboBox.getSelectedItemDisplayName();
746        nxPairs.setMemoryOption((memoryName == null) ? "" : memoryName);
747
748        optionsFrame.setVisible(false);
749
750    }
751
752    private final static Logger log = LoggerFactory.getLogger(AddEntryExitPairPanel.class);
753}