001package jmri.jmrit.signalling;
002
003import java.awt.BorderLayout;
004import java.awt.Component;
005import java.awt.Container;
006import java.awt.Dimension;
007import java.awt.FlowLayout;
008import java.awt.GridLayout;
009import java.awt.event.ActionEvent;
010import java.beans.PropertyChangeListener;
011import java.util.ArrayList;
012import java.util.Hashtable;
013import java.util.List;
014import java.util.Vector;
015
016import javax.swing.BorderFactory;
017import javax.swing.Box;
018import javax.swing.BoxLayout;
019import javax.swing.ButtonGroup;
020import javax.swing.JButton;
021import javax.swing.JCheckBox;
022import javax.swing.JComboBox;
023import javax.swing.JFrame;
024import javax.swing.JLabel;
025import javax.swing.JPanel;
026import javax.swing.JRadioButton;
027import javax.swing.JScrollPane;
028import javax.swing.JTabbedPane;
029import javax.swing.JTable;
030import javax.swing.SortOrder;
031import javax.swing.SwingUtilities;
032import javax.swing.table.AbstractTableModel;
033import javax.swing.table.TableColumn;
034import javax.swing.table.TableColumnModel;
035import javax.swing.table.TableRowSorter;
036
037import jmri.*;
038import jmri.NamedBean.DisplayOptions;
039import jmri.implementation.SignalSpeedMap;
040import jmri.jmrit.beantable.RowComboBoxPanel;
041import jmri.jmrit.display.layoutEditor.LayoutBlockConnectivityTools;
042import jmri.jmrit.display.layoutEditor.LayoutBlockManager;
043import jmri.swing.NamedBeanComboBox;
044import jmri.swing.RowSorterUtil;
045import jmri.util.AlphanumComparator;
046import jmri.util.swing.JComboBoxUtil;
047import jmri.util.swing.JmriJOptionPane;
048import jmri.util.swing.JmriPanel;
049
050/**
051 * Create a JFrame to configure Signal Mast Logic Pairs (Source + Destination
052 * Masts).
053 *
054 * @author Kevin Dickerson Copyright (C) 2011
055 * @author Egbert Broerse Copyright (C) 2017, 2018, 2019
056 */
057public class SignallingPanel extends JmriPanel {
058
059    private final NamedBeanComboBox<SignalMast> sourceMastBox;
060    private final NamedBeanComboBox<SignalMast> destMastBox;
061    private JLabel fixedSourceMastLabel = new JLabel();
062    private JLabel fixedDestMastLabel = new JLabel();
063    private static final JLabel sourceMastLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("SourceMast")), JLabel.TRAILING);  // NOI18N
064    private static final JLabel destMastLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("DestMast")), JLabel.TRAILING);  // NOI18N
065    private final JCheckBox useLayoutEditor = new JCheckBox(Bundle.getMessage("UseLayoutEditorPaths"));  // NOI18N
066    private final JCheckBox useLayoutEditorTurnout = new JCheckBox(Bundle.getMessage("UseTurnoutDetails"));  // NOI18N
067    private final JCheckBox useLayoutEditorBlock = new JCheckBox(Bundle.getMessage("UseBlockDetails"));  // NOI18N
068    private final JCheckBox allowAutoMastGeneration = new JCheckBox(Bundle.getMessage("AllowAutomaticSignalMast"));  // NOI18N
069    private final JCheckBox lockTurnouts = new JCheckBox(Bundle.getMessage("LockTurnouts"));  // NOI18N
070    private static final JButton sizer = new JButton("Sizer");  // NOI18N
071
072    // fields to store the items currently being configured
073    private SignalMast sourceMast;
074    private SignalMast destMast;
075    private SignalMastLogic sml;
076
077    private final jmri.NamedBeanHandleManager nbhm = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class);
078
079    private JFrame jFrame;
080
081    // Size of the individual bean tables inside the shared pane
082    private static final Dimension TABLESIZEPREFERRED = new Dimension(720, 200);
083
084    /**
085     * Create an empty JPanel to configure a new Signal Mast Logic.
086     *
087     * @param frame Name for the enclosing JFrame
088     */
089    public SignallingPanel(JFrame frame) {
090        this(null, null, frame);
091    }
092
093    /**
094     * Create and fill in the JPanel to edit an existing Signal Mast Logic.
095     *
096     * @see SignallingFrame
097     * @param source Bean of Source Signal Mast
098     * @param dest   Bean of Destination Signal Mast
099     * @param frame  Name for the enclosing JFrame
100     */
101    public SignallingPanel(SignalMast source, SignalMast dest, JFrame frame) {
102        super();
103        jFrame = frame;
104        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));  // NOI18N
105        JButton updateButton = new JButton(Bundle.getMessage("UpdateLogicButton"));  // NOI18N
106        JButton applyButton = new JButton(Bundle.getMessage("ButtonApply"));  // NOI18N
107        JLabel mastSpeed = new JLabel();
108
109        if (source != null) {
110            this.sourceMast = source;
111            this.sml = InstanceManager.getDefault(jmri.SignalMastLogicManager.class).getSignalMastLogic(source);
112            fixedSourceMastLabel = new JLabel(sourceMast.getDisplayName());
113            // if (dest != null) {
114            //   frame.setTitle(source.getDisplayName() + " to " + dest.getDisplayName());
115            // }
116        }
117        if ((dest != null) && (sml != null)) {
118            this.destMast = dest;
119            if (!sml.isDestinationValid(dest)) {
120                sml.setDestinationMast(dest);
121            }
122            fixedDestMastLabel = new JLabel(destMast.getDisplayName());
123            useLayoutEditor.setSelected(sml.useLayoutEditor(destMast));
124            useLayoutEditorTurnout.setSelected(sml.useLayoutEditorTurnouts(destMast));
125            useLayoutEditorBlock.setSelected(sml.useLayoutEditorBlocks(destMast));
126            allowAutoMastGeneration.setSelected(sml.allowAutoMaticSignalMastGeneration(destMast));
127            lockTurnouts.setSelected(sml.isTurnoutLockAllowed(destMast));
128
129            float pathSpeed = sml.getMaximumSpeed(dest);
130            if (pathSpeed == 0.0f) {
131                mastSpeed.setText(Bundle.getMessage("MakeLabel", Bundle.getMessage("PathSpeed")) + " " + Bundle.getMessage("NoneSet"));  // NOI18N
132            } else {
133                String speed = jmri.InstanceManager.getDefault(SignalSpeedMap.class).getNamedSpeed(pathSpeed);
134                if (speed != null) {
135                    mastSpeed.setText(Bundle.getMessage("MakeLabel", Bundle.getMessage("PathSpeed")) + " " + speed);  // NOI18N
136                } else {
137                    mastSpeed.setText(Bundle.getMessage("MakeLabel", Bundle.getMessage("PathSpeed")) + " " + pathSpeed);  // NOI18N
138                }
139            }
140        } else if (dest == null) {
141            sml = null;
142        }
143
144        SignalMastManager smm = InstanceManager.getDefault(jmri.SignalMastManager.class);
145        sourceMastBox = new NamedBeanComboBox<>(smm, sourceMast, DisplayOptions.DISPLAYNAME);
146        sourceMastBox.setMaximumSize(sourceMastBox.getPreferredSize());
147        destMastBox = new NamedBeanComboBox<>(smm, destMast, DisplayOptions.DISPLAYNAME);
148        destMastBox.setMaximumSize(destMastBox.getPreferredSize());
149
150        JComboBoxUtil.setupComboBoxMaxRows(sourceMastBox);
151        JComboBoxUtil.setupComboBoxMaxRows(destMastBox);
152
153        // directly add sub-panes onto JFrame's content pane to allow resizing (2018)
154        Container contentPane = frame.getContentPane();
155
156        JPanel header = new JPanel();
157        header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS));
158
159        JPanel mastGrid = new JPanel();
160        GridLayout layout = new GridLayout(2, 2, 10, 0); // (int rows, int cols, int hgap, int vgap)
161        mastGrid.setLayout(layout);
162        // row 1
163        mastGrid.add(sourceMastLabel);
164
165        JPanel sourcePanel = new JPanel();
166        sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
167        sourcePanel.add(sourceMastBox);
168        sourcePanel.add(fixedSourceMastLabel);
169        mastGrid.add(sourcePanel);
170        // row 2
171        mastGrid.add(destMastLabel);
172
173        JPanel destPanel = new JPanel();
174        destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.X_AXIS));
175        destPanel.add(destMastBox);
176        destPanel.add(fixedDestMastLabel);
177
178        destMastBox.addActionListener(e -> {
179            if (useLayoutEditor.isSelected()) {
180                try {
181                    boolean valid = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlockConnectivityTools().checkValidDest(sourceMastBox.getSelectedItem(),
182                            destMastBox.getSelectedItem(), LayoutBlockConnectivityTools.Routing.MASTTOMAST);
183                    if (!valid) {
184                        JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorUnReachableDestination"));
185                    }
186                } catch (jmri.JmriException je) {
187                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("WarningUnableToValidate"));
188                }
189            }
190        });
191
192        mastGrid.add(destPanel);
193        header.add(mastGrid);
194
195        header.add(mastSpeed);
196
197        JPanel editor = new JPanel();
198        editor.setLayout(new BoxLayout(editor, BoxLayout.Y_AXIS));
199        useLayoutEditor.setAlignmentX(Component.LEFT_ALIGNMENT);
200        editor.add(useLayoutEditor);
201
202        JPanel useLayoutEditorSubPanel = new JPanel(); // indent 2 options connected to LayoutEditor choice
203        useLayoutEditorSubPanel.setLayout(new BoxLayout(useLayoutEditorSubPanel, BoxLayout.Y_AXIS));
204        useLayoutEditorSubPanel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
205        useLayoutEditorSubPanel.add(useLayoutEditorTurnout);
206        useLayoutEditorSubPanel.add(useLayoutEditorBlock);
207        editor.add(useLayoutEditorSubPanel);
208        useLayoutEditorSubPanel.setVisible(false);
209
210        useLayoutEditor.addActionListener(e -> {
211
212            useLayoutEditorSubPanel.setVisible(useLayoutEditor.isSelected());
213            // Setup for display of all Turnouts, if needed
214            boolean valid;
215            if (useLayoutEditor.isSelected()) {
216                jFrame.pack();
217                if (!InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()) {
218                    int response = JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("EnableLayoutBlockRouting"),
219                        Bundle.getMessage("QuestionTitle"), JmriJOptionPane.QUESTION_MESSAGE);
220                    if (response == JmriJOptionPane.YES_OPTION) {
221                        InstanceManager.getDefault(LayoutBlockManager.class).enableAdvancedRouting(true);
222                        JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("LayoutBlockRoutingEnabled"));  // NOI18N
223                    }
224                }
225
226                if ((sml != null) && (destMast != null)) {
227                    try {
228                        sml.useLayoutEditor(useLayoutEditor.isSelected(), destMast);
229                    } catch (jmri.JmriException je) {
230                        JmriJOptionPane.showMessageDialog(this, je.toString());
231                    }
232                    try {
233                        valid = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlockConnectivityTools().checkValidDest(sourceMastBox.getSelectedItem(),
234                                destMastBox.getSelectedItem(), LayoutBlockConnectivityTools.Routing.MASTTOMAST);
235                        if (!valid) {
236                            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorUnReachableDestination"));
237                        }
238                    } catch (jmri.JmriException je) {
239                        JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("WarningUnableToValidate"));
240                    }
241                }
242            }
243        });
244        header.add(editor);
245        header.add(allowAutoMastGeneration);
246        header.add(lockTurnouts);
247
248        // selection radiobuttons for All/Included items
249        JPanel py = new JPanel();
250        py.add(new JLabel(Bundle.getMessage("Show")));  // NOI18N
251        ButtonGroup selGroup = new ButtonGroup();
252        allButton = new JRadioButton(Bundle.getMessage("All"), true);  // NOI18N
253        selGroup.add(allButton);
254        py.add(allButton);
255        allButton.addActionListener(e -> {
256            // Setup for display of all Turnouts, if needed
257            if (!showAll) {
258                showAll = true;
259                _blockModel.fireTableDataChanged();
260                _turnoutModel.fireTableDataChanged();
261                _signalMastModel.fireTableDataChanged();
262                _sensorModel.fireTableDataChanged();
263            }
264        });
265        JRadioButton includedButton = new JRadioButton(Bundle.getMessage("Included"), false);  // NOI18N
266        selGroup.add(includedButton);
267        py.add(includedButton);
268        includedButton.addActionListener(e -> {
269            // Setup for display of included Turnouts only, if needed
270            if (showAll) {
271                showAll = false;
272                initializeIncludedList();
273                _blockModel.fireTableDataChanged();
274                _turnoutModel.fireTableDataChanged();
275                _signalMastModel.fireTableDataChanged();
276                _sensorModel.fireTableDataChanged();
277            }
278        });
279        py.add(new JLabel("  " + Bundle.getMessage("Elements")));  // NOI18N
280        header.add(py);
281        contentPane.add(header);
282
283        // build_x_Panel() returns a JScrollFrame
284        JTabbedPane detailsTab = new JTabbedPane();
285        detailsTab.add(Bundle.getMessage("Blocks"), buildBlocksPanel());  // NOI18N
286        detailsTab.add(Bundle.getMessage("Turnouts"), buildTurnoutPanel());  // NOI18N
287        detailsTab.add(Bundle.getMessage("Sensors"), buildSensorPanel());  // NOI18N
288        detailsTab.add(Bundle.getMessage("SignalMasts"), buildSignalMastPanel());  // NOI18N
289
290        JScrollPane detailsScrollPane = new JScrollPane(detailsTab); // make set of 1-2 tables scrollable on smaller screens
291        contentPane.add(detailsScrollPane);
292
293        JPanel footer = new JPanel();
294        footer.setLayout(new FlowLayout(FlowLayout.TRAILING));
295
296        // Cancel button
297        footer.add(cancelButton);
298        cancelButton.addActionListener(this::cancelPressed);
299
300        // Update button
301        footer.add(updateButton);
302        updateButton.addActionListener(this::updatePressed);
303        updateButton.setToolTipText(Bundle.getMessage("UpdateButtonToolTip"));  // NOI18N
304        updateButton.setVisible(true);
305
306        // Apply (and Close) button
307        footer.add(applyButton);
308        applyButton.addActionListener(this::applyPressed);
309        applyButton.setToolTipText(Bundle.getMessage("ApplyButtonToolTip"));  // NOI18N
310        applyButton.setVisible(true);
311
312        contentPane.add(Box.createVerticalGlue()); // glue above buttons
313        contentPane.add(footer);
314
315        if (sourceMast != null) { // edit an existing SML, fix source mast
316            fixedSourceMastLabel.setVisible(true);
317            sourceMastBox.setVisible(false);
318        } else { // source mast selectable for a new SML
319            fixedSourceMastLabel.setVisible(false);
320            sourceMastBox.setVisible(true);
321        }
322        if ((sml != null) && (destMast != null)) {  // edit an existing SML, fix destination mast
323            fixedDestMastLabel.setVisible(true);
324            destMastBox.setVisible(false);
325            useLayoutEditorSubPanel.setVisible(useLayoutEditor.isSelected());
326            initializeIncludedList();
327            editDetails(); // pick up details for an existing SML configuration
328        } else {
329            useLayoutEditorSubPanel.setVisible(useLayoutEditor.isSelected());
330            fixedDestMastLabel.setVisible(false);
331            destMastBox.setVisible(true);
332        }
333    }
334
335    private JScrollPane _manualBlockScrollPane;
336    private JScrollPane _manualSignalMastScrollPane;
337    private JScrollPane _manualSensorScrollPane;
338
339    private BlockModel _blockModel;
340    private AutoBlockModel _autoBlockModel;
341    private List<ManualBlockList> _manualBlockList;
342    private List<AutoBlockList> _automaticBlockList = new ArrayList<>();
343
344    private TurnoutModel _turnoutModel;
345    private AutoTurnoutModel _autoTurnoutModel;
346    private List<ManualTurnoutList> _manualTurnoutList;
347    private List<AutoTurnoutList> _automaticTurnoutList = new ArrayList<>();
348
349    private SensorModel _sensorModel;
350    private List<ManualSensorList> _manualSensorList;
351
352    private SignalMastModel _signalMastModel;
353    private AutoMastModel _autoSignalMastModel;
354    private List<ManualSignalMastList> _manualSignalMastList;
355    private List<AutoSignalMastList> _automaticSignalMastList = new ArrayList<>();
356
357    private final JPanel p2xb = new JPanel();
358
359    /**
360     * Compose GUI for setting up Blocks tab for an SML.
361     *
362     * @return a JPanel containing the SML control blocks configuration
363     *         interface
364     */
365    private JPanel buildBlocksPanel() {
366        JPanel blockPanel = new JPanel();
367        blockPanel.setLayout(new BoxLayout(blockPanel, BoxLayout.Y_AXIS));
368
369        jmri.BlockManager bm = jmri.InstanceManager.getDefault(jmri.BlockManager.class);
370        _manualBlockList = new ArrayList<>();
371        for (Block b : bm.getNamedBeanSet()) {
372            _manualBlockList.add(new ManualBlockList(b));
373        }
374
375        if ((sml != null) && (destMast != null)) {
376            List<Block> blkList = sml.getAutoBlocks(destMast);
377            _automaticBlockList = new ArrayList<>(blkList.size());
378            for (Block blk : blkList) {
379                AutoBlockList blockitem = new AutoBlockList(blk);
380                blockitem.setState(sml.getAutoBlockState(blk, destMast));
381                _automaticBlockList.add(blockitem);
382            }
383        }
384        JPanel p2xc = new JPanel();  // this hides a field
385        JPanel p2xcSpace = new JPanel();
386        p2xcSpace.setLayout(new BoxLayout(p2xcSpace, BoxLayout.Y_AXIS));
387        p2xcSpace.add(new JLabel("XXX"));  // NOI18N
388        p2xc.add(p2xcSpace);
389
390        JPanel p21c = new JPanel();
391        p21c.setLayout(new BoxLayout(p21c, BoxLayout.Y_AXIS));
392        p21c.add(new JLabel(Bundle.getMessage("LabelSelectChecked", Bundle.getMessage("Blocks"))));  // NOI18N
393        p2xc.add(p21c);
394
395        _blockModel = new BlockModel();
396        JTable manualBlockTable = new JTable(_blockModel);
397        TableRowSorter<BlockModel> manualBlockSorter = new TableRowSorter<>(_blockModel);
398        // configure row height for comboBox
399        manualBlockTable.setRowHeight(sizer.getPreferredSize().height - 2); // row height has to be greater than for plain tables
400        manualBlockSorter.setComparator(BlockModel.SNAME_COLUMN, new jmri.util.AlphanumComparator());
401        manualBlockSorter.setComparator(BlockModel.UNAME_COLUMN, new jmri.util.AlphanumComparator());
402        RowSorterUtil.setSortOrder(manualBlockSorter, BlockModel.SNAME_COLUMN, SortOrder.ASCENDING);
403        _blockModel.configStateColumn(manualBlockTable); // create static comboBox in State column
404        manualBlockTable.setRowSorter(manualBlockSorter);
405        manualBlockTable.setRowSelectionAllowed(false);
406        manualBlockTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
407        // JComboBox<String> stateCCombo = new JComboBox<>(); // moved to ManualBlockTable class
408
409        TableColumnModel _manualBlockColumnModel = manualBlockTable.getColumnModel();
410        TableColumn includeColumnC = _manualBlockColumnModel.
411                getColumn(BlockModel.INCLUDE_COLUMN);
412        includeColumnC.setResizable(false);
413        includeColumnC.setMinWidth(9 * Bundle.getMessage("Include").length()); // was fixed 60  // NOI18N
414        includeColumnC.setMaxWidth(includeColumnC.getMinWidth() + 5);
415
416        TableColumn sNameColumnC = _manualBlockColumnModel.
417                getColumn(BlockModel.SNAME_COLUMN);
418        sNameColumnC.setResizable(true);
419        sNameColumnC.setMinWidth(75);
420
421        TableColumn stateColumnC = _manualBlockColumnModel.
422                getColumn(BlockModel.STATE_COLUMN);
423        //stateColumnC.setCellEditor(new DefaultCellEditor(stateCCombo)); // moved to ManualBlockTable class
424        stateColumnC.setResizable(false);
425        stateColumnC.setMinWidth(9 * Math.max(SET_TO_UNOCCUPIED.length(), SET_TO_OCCUPIED.length()) + 40);
426        stateColumnC.setMaxWidth(stateColumnC.getMinWidth() + 10); // was fixed 100
427        // remaining space is filled by UserName
428        _manualBlockScrollPane = new JScrollPane(manualBlockTable);
429        p2xc.add(_manualBlockScrollPane, BorderLayout.CENTER);
430        blockPanel.add(p2xc);
431        p2xc.setVisible(true);
432
433        setRowHeight(manualBlockTable.getRowHeight());
434        p2xcSpace.setVisible(false);
435
436        JPanel p2xaSpace = new JPanel();
437        p2xaSpace.setLayout(new BoxLayout(p2xaSpace, BoxLayout.Y_AXIS));
438        p2xaSpace.add(new JLabel("XXX"));  // NOI18N
439        p2xb.add(p2xaSpace);
440
441        JPanel p21a = new JPanel();
442        p21a.setLayout(new BoxLayout(p21a, BoxLayout.Y_AXIS));
443        p21a.add(new JLabel(Bundle.getMessage("LabelAutogenerated", Bundle.getMessage("Blocks"))));  // NOI18N
444        p2xb.add(p21a);
445
446        _autoBlockModel = new AutoBlockModel();
447        JTable autoBlockTable = new JTable(_autoBlockModel);
448        TableRowSorter<AutoBlockModel> autoBlockSorter = new TableRowSorter<>(_autoBlockModel);
449        autoBlockSorter.setComparator(AutoBlockModel.SNAME_COLUMN, new jmri.util.AlphanumComparator());
450        autoBlockSorter.setComparator(AutoBlockModel.UNAME_COLUMN, new jmri.util.AlphanumComparator());
451        RowSorterUtil.setSortOrder(autoBlockSorter, AutoBlockModel.SNAME_COLUMN, SortOrder.ASCENDING);
452        autoBlockTable.setRowSorter(autoBlockSorter);
453        autoBlockTable.setRowSelectionAllowed(false);
454        autoBlockTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
455
456        TableColumnModel _autoBlockColumnModel = autoBlockTable.getColumnModel();
457        TableColumn sNameColumnA = _autoBlockColumnModel.
458                getColumn(AutoBlockModel.SNAME_COLUMN);
459        sNameColumnA.setResizable(true);
460        sNameColumnA.setMinWidth(75);
461
462        TableColumn stateColumnA = _autoBlockColumnModel.
463                getColumn(AutoBlockModel.STATE_COLUMN);
464        stateColumnA.setResizable(false);
465        stateColumnA.setMinWidth(90);
466        stateColumnA.setMaxWidth(100);
467
468        JScrollPane _autoBlockScrollPane = new JScrollPane(autoBlockTable);
469        p2xb.add(_autoBlockScrollPane, BorderLayout.CENTER);
470        blockPanel.add(p2xb);
471        p2xb.setVisible(true);
472
473        setRowHeight(autoBlockTable.getRowHeight());
474        p2xaSpace.setVisible(false);
475
476        return blockPanel;
477    }
478
479    private final JPanel p2xa = new JPanel();
480
481    /**
482     * Compose GUI for setting up the Turnouts tab for an SML.
483     *
484     * @return a JPanel containing the SML control turnouts configuration
485     *         interface
486     */
487    private JPanel buildTurnoutPanel() {
488        JPanel turnoutPanel = new JPanel();
489        turnoutPanel.setLayout(new BoxLayout(turnoutPanel, BoxLayout.Y_AXIS));
490
491        jmri.TurnoutManager bm = jmri.InstanceManager.turnoutManagerInstance();
492        _manualTurnoutList = new ArrayList<>();
493        for (Turnout b : bm.getNamedBeanSet()) {
494            String systemName = b.getSystemName();
495            String userName = b.getUserName();
496            _manualTurnoutList.add(new ManualTurnoutList(systemName, userName));
497        }
498
499        if ((sml != null) && (destMast != null)) {
500            List<Turnout> turnList = sml.getAutoTurnouts(destMast);
501            _automaticTurnoutList = new ArrayList<>(turnList.size());
502            for (Turnout turn : turnList) {
503                String systemName = turn.getSystemName();
504                String userName = turn.getUserName();
505                AutoTurnoutList turnItem = new AutoTurnoutList(systemName, userName);
506                turnItem.setState(sml.getAutoTurnoutState(turn, destMast));
507                _automaticTurnoutList.add(turnItem);
508            }
509        }
510
511        JPanel p2xt = new JPanel();
512        JPanel p2xcSpace = new JPanel();
513        p2xcSpace.setLayout(new BoxLayout(p2xcSpace, BoxLayout.Y_AXIS));
514        p2xcSpace.add(new JLabel("XXX"));  // NOI18N
515        p2xt.add(p2xcSpace);
516
517        JPanel p21c = new JPanel();
518        p21c.setLayout(new BoxLayout(p21c, BoxLayout.Y_AXIS));
519        p21c.add(new JLabel(Bundle.getMessage("LabelSelectChecked", Bundle.getMessage("Turnouts"))));  // NOI18N
520        p2xt.add(p21c);
521
522        _turnoutModel = new TurnoutModel();
523        JTable manualTurnoutTable = new JTable(_turnoutModel);
524        TableRowSorter<TurnoutModel> manualTurnoutSorter = new TableRowSorter<>(_turnoutModel);
525        // configure row height for comboBox
526        manualTurnoutTable.setRowHeight(sizer.getPreferredSize().height - 2); // row height has to be greater than for plain tables
527        manualTurnoutSorter.setComparator(TurnoutModel.SNAME_COLUMN, new AlphanumComparator());
528        manualTurnoutSorter.setComparator(TurnoutModel.UNAME_COLUMN, new AlphanumComparator());
529        RowSorterUtil.setSortOrder(manualTurnoutSorter, TurnoutModel.SNAME_COLUMN, SortOrder.ASCENDING);
530        _turnoutModel.configStateColumn(manualTurnoutTable); // create static comboBox in State column
531        manualTurnoutTable.setRowSorter(manualTurnoutSorter);
532        manualTurnoutTable.setRowSelectionAllowed(false);
533        manualTurnoutTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
534        // JComboBox<String> stateCCombo = new JComboBox<>(); // moved to ManualTurnoutTable class
535
536        TableColumnModel _manualTurnoutColumnModel = manualTurnoutTable.getColumnModel();
537        TableColumn includeColumnC = _manualTurnoutColumnModel.
538                getColumn(TurnoutModel.INCLUDE_COLUMN);
539        includeColumnC.setResizable(false);
540        includeColumnC.setMinWidth(9 * Bundle.getMessage("Include").length()); // was fixed 60  // NOI18N
541        includeColumnC.setMaxWidth(includeColumnC.getMinWidth() + 5);
542
543        TableColumn sNameColumnC = _manualTurnoutColumnModel.
544                getColumn(TurnoutModel.SNAME_COLUMN);
545        sNameColumnC.setResizable(true);
546        sNameColumnC.setMinWidth(75);
547
548        TableColumn stateColumnC = _manualTurnoutColumnModel.
549                getColumn(TurnoutModel.STATE_COLUMN);
550        // stateColumnC.setCellEditor(new DefaultCellEditor(stateCCombo)); // moved to ManualTurnoutTable class
551        stateColumnC.setResizable(false);
552        log.debug("L = {}", SET_TO_ANY.length());
553        stateColumnC.setMinWidth(9 * Math.max(SET_TO_ANY.length(), SET_TO_CLOSED.length()) + 30);
554        stateColumnC.setMaxWidth(stateColumnC.getMinWidth() + 10); // was fixed 100
555        // remaining space is filled by UserName
556        JScrollPane _manualTurnoutScrollPane = new JScrollPane(manualTurnoutTable);
557        p2xt.add(_manualTurnoutScrollPane, BorderLayout.CENTER);
558        turnoutPanel.add(p2xt);
559        p2xt.setVisible(true);
560
561        ROW_HEIGHT = manualTurnoutTable.getRowHeight();
562        p2xcSpace.setVisible(false);
563
564        JPanel p2xaSpace = new JPanel();
565        p2xaSpace.setLayout(new BoxLayout(p2xaSpace, BoxLayout.Y_AXIS));
566        p2xaSpace.add(new JLabel("XXX"));  // NOI18N
567        p2xa.add(p2xaSpace);
568
569        JPanel p21a = new JPanel();
570        p21a.setLayout(new BoxLayout(p21a, BoxLayout.Y_AXIS));
571        p21a.add(new JLabel(Bundle.getMessage("LabelAutogenerated", Bundle.getMessage("Turnouts"))));  // NOI18N
572        p2xa.add(p21a);
573
574        _autoTurnoutModel = new AutoTurnoutModel();
575        JTable autoTurnoutTable = new JTable(_autoTurnoutModel);
576        TableRowSorter<AutoTurnoutModel> autoTurnoutSorter = new TableRowSorter<>(_autoTurnoutModel);
577        autoTurnoutSorter.setComparator(AutoTurnoutModel.SNAME_COLUMN, new AlphanumComparator());
578        autoTurnoutSorter.setComparator(AutoTurnoutModel.UNAME_COLUMN, new AlphanumComparator());
579        RowSorterUtil.setSortOrder(autoTurnoutSorter, AutoTurnoutModel.SNAME_COLUMN, SortOrder.ASCENDING);
580        autoTurnoutTable.setRowSorter(autoTurnoutSorter);
581        autoTurnoutTable.setRowSelectionAllowed(false);
582        autoTurnoutTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
583
584        TableColumnModel _autoTurnoutColumnModel = autoTurnoutTable.getColumnModel();
585        TableColumn sNameColumnA = _autoTurnoutColumnModel.
586                getColumn(AutoTurnoutModel.SNAME_COLUMN);
587        sNameColumnA.setResizable(true);
588        sNameColumnA.setMinWidth(75);
589
590        TableColumn stateColumnA = _autoTurnoutColumnModel.
591                getColumn(AutoTurnoutModel.STATE_COLUMN);
592        stateColumnA.setResizable(false);
593        stateColumnA.setMinWidth(90);
594        stateColumnA.setMaxWidth(100);
595
596        JScrollPane _autoTurnoutScrollPane = new JScrollPane(autoTurnoutTable);
597        p2xa.add(_autoTurnoutScrollPane, BorderLayout.CENTER);
598        turnoutPanel.add(p2xa);
599        p2xa.setVisible(true);
600
601        ROW_HEIGHT = autoTurnoutTable.getRowHeight();
602        p2xaSpace.setVisible(false);
603
604        return turnoutPanel;
605    }
606
607    /**
608     * Compose GUI for setting up the Sensors tab for an SML.
609     *
610     * @return a JPanel containing the SML control sensors configuration
611     *         interface
612     */
613    private JPanel buildSensorPanel() {
614        JPanel sensorPanel = new JPanel();
615        sensorPanel.setLayout(new BoxLayout(sensorPanel, BoxLayout.Y_AXIS));
616
617        jmri.SensorManager bm = jmri.InstanceManager.sensorManagerInstance();
618        _manualSensorList = new ArrayList<>();
619        for (Sensor ss : bm.getNamedBeanSet()) {
620            String systemName = ss.getSystemName();
621            String userName = ss.getUserName();
622            _manualSensorList.add(new ManualSensorList(systemName, userName));
623        }
624
625        JPanel p2xs = new JPanel();
626        JPanel p2xsSpace = new JPanel();
627        p2xsSpace.setLayout(new BoxLayout(p2xsSpace, BoxLayout.Y_AXIS));
628        p2xsSpace.add(new JLabel("XXX"));  // NOI18N
629        p2xs.add(p2xsSpace);
630
631        JPanel p21c = new JPanel();
632        p21c.setLayout(new BoxLayout(p21c, BoxLayout.Y_AXIS));
633        p21c.add(new JLabel(Bundle.getMessage("LabelSelectChecked", Bundle.getMessage("Sensors"))));  // NOI18N
634        p2xs.add(p21c);
635
636        _sensorModel = new SensorModel();
637        JTable manualSensorTable = new JTable(_sensorModel);
638        TableRowSorter<SensorModel> manualSensorSorter = new TableRowSorter<>(_sensorModel);
639        // configure row height for comboBox
640        manualSensorTable.setRowHeight(sizer.getPreferredSize().height - 2); // row height has to be greater than for plain tables
641        manualSensorSorter.setComparator(SensorModel.SNAME_COLUMN, new AlphanumComparator());
642        manualSensorSorter.setComparator(SensorModel.UNAME_COLUMN, new AlphanumComparator());
643        RowSorterUtil.setSortOrder(manualSensorSorter, SensorModel.SNAME_COLUMN, SortOrder.ASCENDING);
644        _sensorModel.configStateColumn(manualSensorTable); // create static comboBox in State column
645        manualSensorTable.setRowSorter(manualSensorSorter);
646        manualSensorTable.setRowSelectionAllowed(false);
647        manualSensorTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
648        //stateCCombo = new JComboBox<>(); // moved to ManualSensorTable class
649
650        TableColumnModel _manualSensorColumnModel = manualSensorTable.getColumnModel();
651        TableColumn includeColumnC = _manualSensorColumnModel.
652                getColumn(SensorModel.INCLUDE_COLUMN);
653        includeColumnC.setResizable(false);
654        includeColumnC.setMinWidth(9 * Bundle.getMessage("Include").length()); // was fixed 60  // NOI18N
655        includeColumnC.setMaxWidth(includeColumnC.getMinWidth() + 5);
656
657        TableColumn sNameColumnC = _manualSensorColumnModel.
658                getColumn(SensorModel.SNAME_COLUMN);
659        sNameColumnC.setResizable(true);
660        sNameColumnC.setMinWidth(75);
661
662        TableColumn stateColumnC = _manualSensorColumnModel.
663                getColumn(SensorModel.STATE_COLUMN);
664        stateColumnC.setResizable(false);
665        stateColumnC.setMinWidth(9 * SET_TO_INACTIVE.length() + 30);
666        stateColumnC.setMaxWidth(stateColumnC.getMinWidth() + 10); // was fixed 100
667        // remaining space is filled by UserName
668        _manualSensorScrollPane = new JScrollPane(manualSensorTable);
669        p2xs.add(_manualSensorScrollPane, BorderLayout.CENTER);
670
671        sensorPanel.add(p2xs);
672        p2xs.setVisible(true);
673
674        ROW_HEIGHT = manualSensorTable.getRowHeight();
675        p2xsSpace.setVisible(false);
676
677        return sensorPanel;
678    }
679
680    private final JPanel p2xsm = new JPanel();
681
682    /**
683     * Compose GUI for setting up the Signal Masts tab for an SML.
684     *
685     * @return a JPanel containing the SML control signal masts configuration
686     *         interface
687     */
688    private JPanel buildSignalMastPanel() {
689        JPanel SignalMastPanel = new JPanel(); // TODO make this a shared variable
690        SignalMastPanel.setLayout(new BoxLayout(SignalMastPanel, BoxLayout.Y_AXIS));
691
692        jmri.SignalMastManager bm = jmri.InstanceManager.getDefault(jmri.SignalMastManager.class);
693        _manualSignalMastList = new ArrayList<>();
694        for (SignalMast m : bm.getNamedBeanSet()) {
695            _manualSignalMastList.add(new ManualSignalMastList(m));
696        }
697
698        JPanel p2xm = new JPanel();
699        JPanel p2xmSpace = new JPanel();
700        p2xmSpace.setLayout(new BoxLayout(p2xmSpace, BoxLayout.Y_AXIS));
701        p2xmSpace.add(new JLabel("XXX"));  // NOI18N
702        p2xm.add(p2xmSpace);
703
704        JPanel p21c = new JPanel();
705        p21c.setLayout(new BoxLayout(p21c, BoxLayout.Y_AXIS));
706        p21c.add(new JLabel(Bundle.getMessage("LabelSelectChecked", Bundle.getMessage("SignalMasts"))));  // NOI18N
707        p2xm.add(p21c);
708
709        _signalMastModel = new SignalMastModel();
710        TableRowSorter<SignalMastModel> manualMastSorter = new TableRowSorter<>(_signalMastModel);
711        JTable manualSignalMastTable = new JTable(_signalMastModel);
712        // configure (extra) row height for comboBox
713        manualSignalMastTable.setRowHeight(sizer.getPreferredSize().height - 2);
714        // row height has to be greater than plain tables to properly show comboBox shape, but tightened a bit over preferred
715        _signalMastModel.configStateColumn(manualSignalMastTable); // create mast (row) specific comboBox in Aspect column
716        manualMastSorter.setComparator(SignalMastModel.SNAME_COLUMN, new jmri.util.AlphanumComparator());
717        manualMastSorter.setComparator(SignalMastModel.UNAME_COLUMN, new jmri.util.AlphanumComparator());
718        RowSorterUtil.setSortOrder(manualMastSorter, SignalMastModel.SNAME_COLUMN, SortOrder.ASCENDING);
719        manualSignalMastTable.setRowSorter(manualMastSorter);
720        manualSignalMastTable.setRowSelectionAllowed(false);
721        manualSignalMastTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
722
723        TableColumnModel _manualSignalMastColumnModel = manualSignalMastTable.getColumnModel();
724        TableColumn includeColumnC = _manualSignalMastColumnModel.
725                getColumn(SignalMastModel.INCLUDE_COLUMN);
726        includeColumnC.setResizable(false);
727        includeColumnC.setMinWidth(9 * Bundle.getMessage("Include").length()); // was fixed 60  // NOI18N
728        includeColumnC.setMaxWidth(includeColumnC.getMinWidth() + 5);
729        TableColumn sNameColumnC = _manualSignalMastColumnModel.
730                getColumn(SignalMastModel.SNAME_COLUMN);
731        sNameColumnC.setResizable(true);
732        sNameColumnC.setMinWidth(75);
733
734        TableColumn stateColumnC = _manualSignalMastColumnModel.
735                getColumn(SensorModel.STATE_COLUMN);
736        stateColumnC.setResizable(false);
737        stateColumnC.setMinWidth(9 * ("Diverging Approach Medium").length() + 20);  // NOI18N
738        stateColumnC.setMaxWidth(stateColumnC.getMinWidth() + 10); // was fixed 100
739        // remaining space is filled by UserName
740        _manualSignalMastScrollPane = new JScrollPane(manualSignalMastTable);
741        p2xm.add(_manualSignalMastScrollPane, BorderLayout.CENTER);
742        SignalMastPanel.add(p2xm);
743        p2xm.setVisible(true);
744
745        ROW_HEIGHT = manualSignalMastTable.getRowHeight();
746        p2xmSpace.setVisible(false);
747
748        JPanel p2xaSpace = new JPanel();
749        p2xaSpace.setLayout(new BoxLayout(p2xaSpace, BoxLayout.Y_AXIS));
750        p2xaSpace.add(new JLabel("XXX"));
751        p2xsm.add(p2xaSpace);
752
753        JPanel p21a = new JPanel();
754        p21a.setLayout(new BoxLayout(p21a, BoxLayout.Y_AXIS));
755        p21a.add(new JLabel(Bundle.getMessage("LabelAutogenerated", Bundle.getMessage("SignalMasts"))));  // NOI18N
756        p2xsm.add(p21a);
757
758        _autoSignalMastModel = new AutoMastModel();
759        JTable autoMastTable = new JTable(_autoSignalMastModel);
760        TableRowSorter<AutoMastModel> autoMastSorter = new TableRowSorter<>(_autoSignalMastModel);
761        autoMastSorter.setComparator(AutoMastModel.SNAME_COLUMN, new jmri.util.AlphanumComparator());
762        autoMastSorter.setComparator(AutoMastModel.UNAME_COLUMN, new jmri.util.AlphanumComparator());
763        RowSorterUtil.setSortOrder(autoMastSorter, AutoMastModel.SNAME_COLUMN, SortOrder.ASCENDING);
764        autoMastTable.setRowSorter(autoMastSorter);
765        autoMastTable.setRowSelectionAllowed(false);
766        autoMastTable.setPreferredScrollableViewportSize(TABLESIZEPREFERRED);
767
768        TableColumnModel _autoMastColumnModel = autoMastTable.getColumnModel();
769        TableColumn sNameColumnA = _autoMastColumnModel.
770                getColumn(AutoMastModel.SNAME_COLUMN);
771        sNameColumnA.setResizable(true);
772        sNameColumnA.setMinWidth(75);
773
774        TableColumn stateColumnA = _autoMastColumnModel.
775                getColumn(AutoMastModel.STATE_COLUMN);
776        stateColumnA.setResizable(false);
777        stateColumnA.setMinWidth(90);
778        stateColumnA.setMaxWidth(100);
779
780        JScrollPane _autoSignalMastScrollPane = new JScrollPane(autoMastTable);
781        p2xsm.add(_autoSignalMastScrollPane, BorderLayout.CENTER);
782        SignalMastPanel.add(p2xsm);
783        p2xsm.setVisible(true);
784
785        ROW_HEIGHT = autoMastTable.getRowHeight();
786        p2xaSpace.setVisible(false);
787
788        return SignalMastPanel;
789    }
790
791    java.beans.PropertyChangeSupport pcs = new java.beans.PropertyChangeSupport(this);
792
793    /**
794     * Update changes in SML when Update button is pressed in the Edit Logic -
795     * Add Logic pane.
796     *
797     * @param e the event heard
798     */
799    private void updatePressed(ActionEvent e) {
800        sourceMast = sourceMastBox.getSelectedItem();
801        destMast = destMastBox.getSelectedItem();
802        boolean smlPairAdded = false;
803        destOK = true;
804
805        if ((sourceMastBox.getSelectedItem() == null) || (destMastBox.getSelectedItem() == null)) {
806            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSignalMastNull",
807                    Bundle.getMessage("SourceMast"), Bundle.getMessage("DestMast")));
808            destOK = false;
809            log.debug("No Source or Destination Mast selected, keep pane open");  // NOI18N
810            return;
811        }
812        if (sourceMast == destMast || fixedSourceMastLabel.getText().equals(destMast.getDisplayName())) {
813            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorSignalMastIdentical"));
814            destOK = false;
815            log.debug("Destination Mast check failed, keep pane open");  // NOI18N
816            return;
817        }
818        if ((sml == null) && (useLayoutEditor.isSelected())) {
819            boolean valid;
820            try {
821                valid = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlockConnectivityTools().checkValidDest(sourceMast,
822                        destMast, LayoutBlockConnectivityTools.Routing.MASTTOMAST);
823                if (!valid) {
824                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorUnReachableDestination"));
825                    return;
826                }
827            } catch (jmri.JmriException je) {
828                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("WarningUnableToValidate"));
829            }
830        }
831
832        if (sml == null) { // a new SML directly from the SML Table
833            sml = InstanceManager.getDefault(jmri.SignalMastLogicManager.class).newSignalMastLogic(sourceMast);
834            // check if a similar SML pair already exists when in Add New session
835            if (!sml.getDestinationList().contains(destMast)) { // not yet defined as a pair
836                smlPairAdded = true;
837                sml.setDestinationMast(destMast);
838            } else {
839                // show replace/update dialog
840                int mes = JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("WarningExistingPair"),
841                        Bundle.getMessage("WarningTitle"), // NOI18N
842                        JmriJOptionPane.YES_NO_OPTION);
843                if (mes != JmriJOptionPane.YES_OPTION) {
844                    return;
845                }
846            }
847            fixedSourceMastLabel.setText(sourceMast.getDisplayName());
848            fixedDestMastLabel.setText(destMast.getDisplayName());
849            sourceMastBox.setVisible(false);
850            destMastBox.setVisible(false);
851            fixedSourceMastLabel.setVisible(true);
852            fixedDestMastLabel.setVisible(true);
853            _autoTurnoutModel.smlValid();
854            _autoBlockModel.smlValid();
855            _autoSignalMastModel.smlValid();
856        }
857        initializeIncludedList();
858        sml.allowAutoMaticSignalMastGeneration(allowAutoMastGeneration.isSelected(), destMast);
859        boolean layoutEditorGen = true;
860        try {
861            sml.useLayoutEditor(useLayoutEditor.isSelected(), destMast);
862        } catch (jmri.JmriException je) {
863            JmriJOptionPane.showMessageDialog(this, je.toString());
864            layoutEditorGen = false;
865        }
866
867        try {
868            if (useLayoutEditor.isSelected()) {
869                sml.useLayoutEditorDetails(useLayoutEditorTurnout.isSelected(), useLayoutEditorBlock.isSelected(), destMast);
870            }
871        } catch (jmri.JmriException ji) {
872            if (layoutEditorGen) {
873                JmriJOptionPane.showMessageDialog(this, ji.toString());
874            }
875        }
876        Hashtable<Block, Integer> hashBlocks = new Hashtable<>();
877        for (ManualBlockList mbl : _includedManualBlockList) {
878            Block blk = jmri.InstanceManager.getDefault(jmri.BlockManager.class).getBlock(mbl.getSysName());
879            if (blk != null) {
880                hashBlocks.put(blk, mbl.getState());
881            }
882        }
883        sml.setBlocks(hashBlocks, destMast);
884
885        Hashtable<NamedBeanHandle<Turnout>, Integer> hashTurnouts = new Hashtable<>();
886        for (ManualTurnoutList mtl : _includedManualTurnoutList) {
887            String turnoutName = mtl.getDisplayName();
888            Turnout turnout = jmri.InstanceManager.turnoutManagerInstance().getTurnout(turnoutName);
889            if (turnout != null) {
890                NamedBeanHandle<Turnout> namedTurnout = nbhm.getNamedBeanHandle(turnoutName, turnout);
891                hashTurnouts.put(namedTurnout, mtl.getState());
892            }
893            // no specific value, just show the current turnout state as selection in comboBox.
894            // for existing SML pair, will be updated to show present setting by editDetails()
895        }
896        sml.setTurnouts(hashTurnouts, destMast);
897
898        Hashtable<NamedBeanHandle<Sensor>, Integer> hashSensors = new Hashtable<>();
899        for (ManualSensorList msl : _includedManualSensorList) {
900            String sensorName = msl.getDisplayName();
901            Sensor sensor = jmri.InstanceManager.sensorManagerInstance().getSensor(msl.getDisplayName());
902            if (sensor != null) {
903                NamedBeanHandle<Sensor> namedSensor = nbhm.getNamedBeanHandle(sensorName, sensor);
904                hashSensors.put(namedSensor, msl.getState());
905            }
906            // no specific value, just show the current sensor state as selection in comboBox.
907            // for existing SML pair, will be updated to show present setting by editDetails()
908        }
909        sml.setSensors(hashSensors, destMast);
910
911        Hashtable<SignalMast, String> hashSignalMasts = new Hashtable<>();
912        for (ManualSignalMastList msml : _includedManualSignalMastList) {
913            if (msml.getMast() == sourceMast || msml.getMast() == destMast) {
914                // warn user that control mast is either source or destination mast of this pair, but allow as a valid choice
915                int mes = JmriJOptionPane.showConfirmDialog(null, java.text.MessageFormat.format(Bundle.getMessage("SignalMastCriteriaOwn"), // NOI18N
916                        msml.getMast().getDisplayName()),
917                        Bundle.getMessage("SignalMastCriteriaOwnTitle"), // NOI18N
918                        JmriJOptionPane.YES_NO_OPTION );
919                if (mes == JmriJOptionPane.YES_OPTION ) {
920                    hashSignalMasts.put(msml.getMast(), msml.getSetToState());
921                } else { // No
922                    msml.setIncluded(false); // deselect "Included" checkBox for signal mast in manualSignalList
923                    initializeIncludedList();
924                    _signalMastModel.fireTableDataChanged();
925                }
926            } else {
927                hashSignalMasts.put(msml.getMast(), msml.getSetToState());
928            }
929        }
930        sml.setMasts(hashSignalMasts, destMast);
931
932        sml.allowTurnoutLock(lockTurnouts.isSelected(), destMast);
933        
934        //required to set up transits using Layout Panel
935        //this.setAssociatedSection(destMast);
936        
937        sml.initialise(destMast);
938        if (smlPairAdded) {
939            log.debug("New SML");  // NOI18N
940            firePropertyChange("newDestination", null, destMastBox.getSelectedItem()); // to show new SML in underlying table  // NOI18N
941        }
942    }
943    
944    void setAssociatedSection(SignalMast destMast){
945        SectionManager sm = InstanceManager.getDefault(SectionManager.class);
946        if (!sml.getAutoBlocksBetweenMasts(destMast).isEmpty()) {
947            String secUserName = sml.getSourceMast().getDisplayName() + ":" + destMast.getDisplayName();
948            Section sec = sm.getSection(secUserName);
949            if (sec != null) {
950                //A Section already exists, lets check that it is one used with the SML, if so carry on using that.
951                if (sec.getSectionType() != Section.SIGNALMASTLOGIC) {
952                    return;
953                }
954            }
955            else {
956                try {
957                    sec = sm.createNewSection(secUserName);
958                }
959                catch(IllegalArgumentException ex){
960                    log.warn("Could not create Section for {} {}",secUserName,ex.getMessage());
961                }
962            }
963            sml.setAssociatedSection(sec, destMast);
964        }
965    }
966
967    private boolean destOK = true; // false indicates destMast and sourceMast are identical
968
969    /**
970     * When Apply button is pressed, call updatePressed and afterwards close the
971     * edit pane.
972     *
973     * @param e the event heard
974     */
975    void applyPressed(ActionEvent e) {
976        updatePressed(e); // store edits
977        if (destOK) { // enable user to correct configuration if warned the destMast is incorrect by skipping pane closing
978            cancelPressed(e); // close panel signaling acceptance of edits/Apply to the user
979        }
980    }
981
982    /**
983     * Clean up when Cancel button is pressed.
984     *
985     * @param e the event heard
986     */
987    void cancelPressed(ActionEvent e) {
988        if (jFrame != null) {
989            jFrame.setVisible(false);
990            jFrame.dispose();
991        }
992        jFrame = null;
993    }
994
995    int blockModeFromBox(JComboBox<String> box) {
996        String mode = (String) box.getSelectedItem();
997        int result = jmri.util.StringUtil.getStateFromName(mode, blockInputModeValues, blockInputModes);
998
999        if (result < 0) {
1000            log.warn("unexpected mode string in blockMode: {}", mode);  // NOI18N
1001            throw new IllegalArgumentException();
1002        }
1003        return result;
1004    }
1005
1006    void setBlockModeBox(int mode, JComboBox<String> box) {
1007        String result = jmri.util.StringUtil.getNameFromState(mode, blockInputModeValues, blockInputModes);
1008        box.setSelectedItem(result);
1009    }
1010
1011    private static final String[] blockInputModes = new String[]{Bundle.getMessage("UnOccupied"), Bundle.getMessage("Occupied")};  // NOI18N
1012    private static final int[] blockInputModeValues = new int[]{Block.UNOCCUPIED, Block.OCCUPIED};
1013
1014    /**
1015     * Create new lists of control items configured as part of an SML.
1016     */
1017    private void initializeIncludedList() {
1018        _includedManualBlockList = new ArrayList<>();
1019        for (ManualBlockList mbl : _manualBlockList) {
1020            if (mbl.isIncluded()) {
1021                _includedManualBlockList.add(mbl);
1022            }
1023        }
1024
1025        if ((sml != null) && (destMast != null)) {
1026            List<Block> blkList = sml.getAutoBlocks(destMast);
1027            _automaticBlockList = new ArrayList<>(blkList.size());
1028            for (Block blk : blkList) {
1029                AutoBlockList newABlk = new AutoBlockList(blk);
1030                _automaticBlockList.add(newABlk);
1031                newABlk.setState(sml.getAutoBlockState(blk, destMast));
1032            }
1033        }
1034
1035        _includedManualTurnoutList = new ArrayList<>();
1036        for (ManualTurnoutList mtl : _manualTurnoutList) {
1037            if (mtl.isIncluded()) {
1038                _includedManualTurnoutList.add(mtl);
1039            }
1040        }
1041
1042        if ((sml != null) && (destMast != null)) {
1043            List<Turnout> turnList = sml.getAutoTurnouts(destMast);
1044            _automaticTurnoutList = new ArrayList<>(turnList.size());
1045            for (Turnout turn : turnList) {
1046                String systemName = turn.getSystemName();
1047                String userName = turn.getUserName();
1048                AutoTurnoutList newAturn = new AutoTurnoutList(systemName, userName);
1049                _automaticTurnoutList.add(newAturn);
1050                newAturn.setState(sml.getAutoTurnoutState(turn, destMast));
1051            }
1052        }
1053
1054        _includedManualSensorList = new ArrayList<>();
1055        for (ManualSensorList msl : _manualSensorList) {
1056            if (msl.isIncluded()) {
1057                _includedManualSensorList.add(msl);
1058            }
1059        }
1060
1061        _includedManualSignalMastList = new ArrayList<>();
1062        for (ManualSignalMastList msml : _manualSignalMastList) {
1063            if (msml.isIncluded()) {
1064                _includedManualSignalMastList.add(msml);
1065            }
1066        }
1067
1068        if ((sml != null) && (destMast != null)) {
1069            List<SignalMast> mastList = sml.getAutoMasts(destMast);
1070            _automaticSignalMastList = new ArrayList<>(mastList.size());
1071            for (SignalMast mast : mastList) {
1072                AutoSignalMastList newAmast = new AutoSignalMastList(mast);
1073                _automaticSignalMastList.add(newAmast);
1074                newAmast.setState(sml.getAutoSignalMastState(mast, destMast));
1075            }
1076        }
1077    }
1078
1079    private final JRadioButton allButton;
1080
1081    private boolean showAll = true;   // false indicates show only included items
1082
1083    private static final String SET_TO_ACTIVE = Bundle.getMessage("SensorStateActive");  // NOI18N
1084    private static final String SET_TO_INACTIVE = Bundle.getMessage("SensorStateInactive");  // NOI18N
1085    private static final String SET_TO_CLOSED = jmri.InstanceManager.turnoutManagerInstance().getClosedText();
1086    private static final String SET_TO_THROWN = jmri.InstanceManager.turnoutManagerInstance().getThrownText();
1087
1088    private static final String SET_TO_UNOCCUPIED = Bundle.getMessage("UnOccupied");  // NOI18N
1089    private static final String SET_TO_OCCUPIED = Bundle.getMessage("Occupied");  // NOI18N
1090    private static final String SET_TO_ANY = Bundle.getMessage("AnyState");  // NOI18N
1091
1092    private static int ROW_HEIGHT;
1093
1094    private static void setRowHeight(int newHeight) {
1095        ROW_HEIGHT = newHeight;
1096    }
1097
1098    /**
1099     * Cancels "Show Included Only" option
1100     */
1101    void cancelIncludedOnly() {
1102        if (!showAll) {
1103            allButton.doClick();
1104        }
1105    }
1106
1107    /**
1108     * Fill in existing SML configuration on the edit panel
1109     */
1110    private void editDetails() {
1111        int setRow = 0;
1112        for (int i = _manualBlockList.size() - 1; i >= 0; i--) {
1113            ManualBlockList block = _manualBlockList.get(i);
1114            String tSysName = block.getSysName();
1115            Block blk = InstanceManager.getDefault(jmri.BlockManager.class).getBlock(tSysName);
1116            if (sml.isBlockIncluded(blk, destMast)) {
1117                block.setIncluded(true);
1118                block.setState(sml.getBlockState(blk, destMast));
1119                setRow = i;
1120            } else {
1121                block.setIncluded(false);
1122                block.setState(Block.UNOCCUPIED);
1123            }
1124        }
1125        setRow -= 1;
1126        if (setRow < 0) {
1127            setRow = 0;
1128        }
1129        _manualBlockScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT);
1130        _blockModel.fireTableDataChanged();
1131
1132        setRow = 0;
1133        for (int i = _manualTurnoutList.size() - 1; i >= 0; i--) {
1134            ManualTurnoutList turnout = _manualTurnoutList.get(i);
1135            String tSysName = turnout.getSysName();
1136            Turnout turn = InstanceManager.turnoutManagerInstance().getTurnout(tSysName);
1137            if (sml.isTurnoutIncluded(turn, destMast)) {
1138                turnout.setIncluded(true);
1139                turnout.setState(sml.getTurnoutState(turn, destMast));
1140                setRow = i;
1141            } else {
1142                turnout.setIncluded(false);
1143                turnout.setState(Turnout.CLOSED);
1144            }
1145        }
1146        setRow -= 1;
1147        if (setRow < 0) {
1148            setRow = 0;
1149        }
1150        _manualSensorScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT);
1151        _sensorModel.fireTableDataChanged();
1152
1153        setRow = 0;
1154        for (int i = _manualSensorList.size() - 1; i >= 0; i--) {
1155            ManualSensorList sensor = _manualSensorList.get(i);
1156            String tSysName = sensor.getSysName();
1157            Sensor sen = InstanceManager.sensorManagerInstance().getSensor(tSysName);
1158            if (sml.isSensorIncluded(sen, destMast)) {
1159                sensor.setIncluded(true);
1160                sensor.setState(sml.getSensorState(sen, destMast));
1161                setRow = i;
1162            } else {
1163                sensor.setIncluded(false);
1164                sensor.setState(Sensor.INACTIVE);
1165            }
1166        }
1167        setRow -= 1;
1168        if (setRow < 0) {
1169            setRow = 0;
1170        }
1171        _manualSensorScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT);
1172        _sensorModel.fireTableDataChanged();
1173
1174        setRow = 0;
1175        for (int i = _manualSignalMastList.size() - 1; i >= 0; i--) {
1176            ManualSignalMastList mast = _manualSignalMastList.get(i);
1177            SignalMast sigMast = _manualSignalMastList.get(i).getMast();
1178            if (sml.isSignalMastIncluded(sigMast, destMast)) {
1179                mast.setIncluded(true);
1180                mast.setSetToState(sml.getSignalMastState(sigMast, destMast));
1181                setRow = i;
1182            } else {
1183                mast.setIncluded(false);
1184            }
1185        }
1186        setRow -= 1;
1187        if (setRow < 0) {
1188            setRow = 0;
1189        }
1190        _manualSignalMastScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT);
1191        _signalMastModel.fireTableDataChanged();
1192
1193    }
1194
1195    private List<ManualBlockList> _includedManualBlockList;
1196    private List<ManualTurnoutList> _includedManualTurnoutList;
1197    private List<ManualSensorList> _includedManualSensorList;
1198    private List<ManualSignalMastList> _includedManualSignalMastList;
1199
1200    /**
1201     * Abstract class implemented during edit of an SML.
1202     */
1203    private abstract static class SignalMastElement {
1204
1205        String _sysName;
1206        String _userName;
1207        boolean _included;
1208        int _setToState;
1209
1210        SignalMastElement() {
1211
1212        }
1213
1214        SignalMastElement(String sysName, String userName) {
1215            _sysName = sysName;
1216            _userName = userName;
1217            _included = false;
1218            _setToState = Sensor.INACTIVE;
1219        }
1220
1221        String getSysName() {
1222            return _sysName;
1223        }
1224
1225        String getUserName() {
1226            return _userName;
1227        }
1228
1229        String getDisplayName() {
1230            String name = getUserName();
1231            if (name != null && name.length() > 0) {
1232                return name;
1233            } else {
1234                return getSysName();
1235            }
1236        }
1237
1238        boolean isIncluded() {
1239            return _included;
1240        }
1241
1242        void setIncluded(boolean include) {
1243            _included = include;
1244        }
1245
1246        abstract String getSetToState();
1247
1248        abstract void setSetToState(String state);
1249
1250        int getState() {
1251            return _setToState;
1252        }
1253
1254        void setState(int state) {
1255            _setToState = state;
1256        }
1257    }
1258
1259    /*
1260     * A series of Lists to store all SML properties during Edit.
1261     */
1262
1263    /**
1264     * A paired list of manually configurable Layout Blocks and a corresponding
1265     * Set To State used during edit of an SML.
1266     */
1267    private static class ManualBlockList extends SignalMastElement {
1268
1269        ManualBlockList(Block block) {
1270            this.block = block;
1271        }
1272        Block block;
1273
1274        @Override
1275        String getSysName() {
1276            return block.getSystemName();
1277        }
1278
1279        @Override
1280        String getUserName() {
1281            return block.getUserName();
1282        }
1283
1284        boolean getPermissiveWorking() {
1285            return block.getPermissiveWorking();
1286        }
1287
1288        String getBlockSpeed() {
1289            return block.getBlockSpeed();
1290        }
1291
1292        @Override
1293        String getSetToState() {
1294            switch (_setToState) {
1295                case Block.OCCUPIED:
1296                    return SET_TO_OCCUPIED;
1297                case Block.UNOCCUPIED:
1298                    return SET_TO_UNOCCUPIED;
1299                default:
1300                    // fall out
1301                    break;
1302            }
1303            return SET_TO_ANY;
1304        }
1305
1306        @Override
1307        void setSetToState(String state) {
1308            if (SET_TO_UNOCCUPIED.equals(state)) {
1309                _setToState = Block.UNOCCUPIED;
1310            } else if (SET_TO_OCCUPIED.equals(state)) {
1311                _setToState = Block.OCCUPIED;
1312            } else {
1313                _setToState = 0x03; // AnyState
1314            }
1315        }
1316    }
1317
1318    /**
1319     * A paired list of automatically configured Layout Blocks and a
1320     * corresponding Set To State used during edit of an SML.
1321     */
1322    private static class AutoBlockList extends ManualBlockList {
1323
1324        AutoBlockList(Block block) {
1325            super(block);
1326        }
1327
1328        @Override
1329        void setSetToState(String state) {
1330        }
1331    }
1332
1333    /**
1334     * A paired list of manually configurable Turnouts and a corresponding Set
1335     * To State used during edit of an SML.
1336     */
1337    private static class ManualTurnoutList extends SignalMastElement {
1338
1339        ManualTurnoutList(String sysName, String userName) {
1340            super(sysName, userName);
1341        }
1342
1343        @Override
1344        String getSetToState() {
1345            switch (_setToState) {
1346                case Turnout.THROWN:
1347                    return SET_TO_THROWN;
1348                case Turnout.CLOSED:
1349                    return SET_TO_CLOSED;
1350                default:
1351                    // fall out
1352                    break;
1353            }
1354            return SET_TO_ANY;
1355        }
1356
1357        @Override
1358        void setSetToState(String state) {
1359            if (SET_TO_THROWN.equals(state)) {
1360                _setToState = Turnout.THROWN;
1361            } else if (SET_TO_CLOSED.equals(state)) {
1362                _setToState = Turnout.CLOSED;
1363            } else {
1364                _setToState = 0x00; // AnyState is not correctly returned with Turnouts
1365            }
1366        }
1367    }
1368
1369    /**
1370     * A paired list of automatically configured Turnouts and a corresponding
1371     * Set To State used during edit of an SML.
1372     */
1373    private static class AutoTurnoutList extends ManualTurnoutList {
1374
1375        AutoTurnoutList(String sysName, String userName) {
1376            super(sysName, userName);
1377        }
1378
1379        @Override
1380        void setSetToState(String state) {
1381        }
1382    }
1383
1384    /**
1385     * A paired list of manually configured Sensors and a corresponding Set To
1386     * State used during edit of an SML.
1387     */
1388    private static class ManualSensorList extends SignalMastElement {
1389
1390        ManualSensorList(String sysName, String userName) {
1391            super(sysName, userName);
1392        }
1393
1394        @Override
1395        String getSetToState() {
1396            switch (_setToState) {
1397                case Sensor.INACTIVE:
1398                    return SET_TO_INACTIVE;
1399                case Sensor.ACTIVE:
1400                    return SET_TO_ACTIVE;
1401                default:
1402                    // fall out
1403                    break;
1404            }
1405            return "";
1406        }
1407
1408        @Override
1409        void setSetToState(String state) {
1410            if (SET_TO_INACTIVE.equals(state)) {
1411                _setToState = Sensor.INACTIVE;
1412            } else if (SET_TO_ACTIVE.equals(state)) {
1413                _setToState = Sensor.ACTIVE;
1414            } // do not provide other choices like "OnChange"
1415        }
1416    }
1417
1418    /**
1419     * A paired list of manually configured Signal Masts and a corresponding Set To
1420     * State used during edit of an SML.
1421     */
1422    private static class ManualSignalMastList extends SignalMastElement {
1423
1424        ManualSignalMastList(SignalMast s) {
1425            mast = s;
1426        }
1427
1428        String _setToAspect = "";
1429
1430        SignalMast mast;
1431
1432        SignalMast getMast() {
1433            return mast;
1434        }
1435
1436        @Override
1437        String getSysName() {
1438            return mast.getSystemName();
1439        }
1440
1441        @Override
1442        String getUserName() {
1443            return mast.getUserName();
1444        }
1445
1446        @Override
1447        String getSetToState() {
1448            return _setToAspect;
1449        }
1450
1451        @Override
1452        void setSetToState(String state) {
1453            _setToAspect = state;
1454        }
1455    }
1456
1457    /**
1458     * A paired list of automatically configured Signal Masts and a
1459     * corresponding Set To State used during edit of an SML.
1460     */
1461    private static class AutoSignalMastList extends ManualSignalMastList {
1462
1463        AutoSignalMastList(SignalMast s) {
1464            super(s);
1465        }
1466
1467        @Override
1468        void setSetToState(String state) {
1469        }
1470
1471        void setState(String state) {
1472            _setToAspect = state;
1473        }
1474    }
1475
1476    /**
1477     * A series of TableModels to display and edit configurations for
1478     * SignalMastLogic (SML) on the Tabs.
1479     */
1480    abstract class TableModel extends AbstractTableModel implements PropertyChangeListener {
1481
1482        @Override
1483        public Class<?> getColumnClass(int c) {
1484            switch (c) {
1485                case INCLUDE_COLUMN:
1486                    return Boolean.class;
1487                case STATE_COLUMN:
1488                    return RowComboBoxPanel.class; // Use a JPanel containing a custom State ComboBox
1489                default:
1490                    return String.class;
1491            }
1492        }
1493
1494        /**
1495         * Respond to change from bean. Prevent State change during edit.
1496         *
1497         * @param e A property change of any bean
1498         */
1499        @Override
1500        public void propertyChange(java.beans.PropertyChangeEvent e) {
1501            if (e.getPropertyName().equals("length")) {  // NOI18N
1502                // a new NamedBean is available in the manager
1503                fireTableDataChanged();
1504            }
1505        }
1506
1507        /**
1508         * Remove references to and from this object, so that it can eventually
1509         * be garbage-collected.
1510         */
1511        public void dispose() {
1512            jmri.InstanceManager.turnoutManagerInstance().removePropertyChangeListener(this);
1513        }
1514
1515        @Override
1516        public String getColumnName(int col) {
1517            switch (col) {
1518                case SNAME_COLUMN:
1519                    return Bundle.getMessage("ColumnSystemName");  // NOI18N
1520                case UNAME_COLUMN:
1521                    return Bundle.getMessage("ColumnUserName");  // NOI18N
1522                case INCLUDE_COLUMN:
1523                    return Bundle.getMessage("Include");  // NOI18N
1524                case STATE_COLUMN:
1525                    return Bundle.getMessage("ColumnState"); // pick up via SignallingBundle as it is a different "State" label than non-signal tables  // NOI18N
1526                default:
1527                    return "unknown";  // NOI18N
1528            }
1529        }
1530
1531        @Override
1532        public int getColumnCount() {
1533            return 4;
1534        }
1535
1536        @Override
1537        public boolean isCellEditable(int r, int c) {
1538            return ((c == INCLUDE_COLUMN) || (c == STATE_COLUMN));
1539        }
1540
1541        /**
1542         * Customize the State column to show an appropriate ComboBox of
1543         * available options.
1544         *
1545         * @param table a JTable of beans
1546         */
1547        protected void configStateColumn(JTable table) {
1548            // have the state column hold a JPanel with a JComboBox for States
1549            // add extras, override BeanTableDataModel
1550            log.debug("Bean configStateColumn (I am {})", super.toString());  // NOI18N
1551            table.setDefaultEditor(RowComboBoxPanel.class, new StateComboBoxPanel());
1552            table.setDefaultRenderer(RowComboBoxPanel.class, new StateComboBoxPanel()); // use same class for the renderer
1553            // Set more things?
1554        }
1555
1556        /**
1557         * Provide a table cell renderer looking like a JComboBox as an
1558         * editor/renderer for the manual tables on all except the Masts tab.
1559         * <p>
1560         * This is a lightweight version of the
1561         * {@link jmri.jmrit.beantable.RowComboBoxPanel} RowComboBox cell editor
1562         * class, some of the hashtables not needed here since we only need
1563         * identical options for all rows in a column.
1564         *
1565         * @see SignalMastModel.AspectComboBoxPanel for a full application with
1566         * row specific comboBox choices.
1567         */
1568        public class StateComboBoxPanel extends RowComboBoxPanel {
1569
1570            @Override
1571            protected final void eventEditorMousePressed() {
1572                this.editor.add(getEditorBox(table.convertRowIndexToModel(this.currentRow))); // add editorBox to JPanel
1573                this.editor.revalidate();
1574                SwingUtilities.invokeLater(this.comboBoxFocusRequester);
1575                log.debug("eventEditorMousePressed in row: {})", this.currentRow);  // NOI18N
1576            }
1577
1578            /**
1579             * Call the method in the surrounding method for the
1580             * SignalHeadTable.
1581             *
1582             * @param row the user clicked on in the table
1583             * @return an appropriate combobox for this signal head
1584             */
1585            @Override
1586            protected JComboBox<String> getEditorBox(int row) {
1587                return getStateEditorBox(row);
1588            }
1589
1590        }
1591
1592        // Methods to display STATE_COLUMN ComboBox in tables.
1593        // All row values are in terms of the Model, not the Table as displayed.
1594        // Hashtables for Editors; none used for Renderers
1595        /**
1596         * Provide a static JComboBox element to display inside the JPanel
1597         * CellEditor. When not yet present, create, store and return a new one.
1598         *
1599         * @param row Index number (in TableDataModel)
1600         * @return A combobox containing the valid aspect names for this mast
1601         */
1602        JComboBox<String> getStateEditorBox(int row) {
1603            // create dummy comboBox, override in extended classes for each bean
1604            JComboBox<String> editCombo = new JComboBox<>();
1605            editCombo.addItem(Bundle.getMessage("None"));  // NOI18N
1606            return editCombo;
1607        }
1608        // end of methods to display STATE_COLUMN ComboBox
1609
1610        public static final int SNAME_COLUMN = 0;
1611        public static final int UNAME_COLUMN = 1;
1612        static final int INCLUDE_COLUMN = 2;
1613        public static final int STATE_COLUMN = 3;
1614    }
1615
1616    /**
1617     * TableModel for selecting SML control Blocks and Block Set To State.
1618     */
1619    class BlockModel extends TableModel {
1620
1621        BlockModel() {
1622            jmri.InstanceManager.getDefault(jmri.BlockManager.class).addPropertyChangeListener(this);
1623        }
1624
1625        @Override
1626        public int getRowCount() {
1627            if (showAll) {
1628                return _manualBlockList.size();
1629            } else {
1630                return _includedManualBlockList.size();
1631            }
1632        }
1633
1634        private static final int SPEED_COLUMN = 4;
1635        private static final int PERMISSIVE_COLUMN = 5;
1636
1637        @Override
1638        public int getColumnCount() {
1639            return 6;
1640        }
1641
1642        @Override
1643        public Object getValueAt(int r, int c) {
1644            List<ManualBlockList> blockList;
1645            if (showAll) {
1646                blockList = _manualBlockList;
1647            } else {
1648                blockList = _includedManualBlockList;
1649            }
1650            // some error checking
1651            if (r >= blockList.size()) {
1652                log.debug("row index is greater than block list");  // NOI18N
1653                return "error";  // NOI18N
1654            }
1655            switch (c) {
1656                case INCLUDE_COLUMN:
1657                    return blockList.get(r).isIncluded();
1658                case SNAME_COLUMN:
1659                    return blockList.get(r).getSysName();
1660                case UNAME_COLUMN:
1661                    return blockList.get(r).getUserName();
1662                case STATE_COLUMN:
1663                    return blockList.get(r).getSetToState();
1664                case SPEED_COLUMN:
1665                    return blockList.get(r).getBlockSpeed();
1666                case PERMISSIVE_COLUMN:
1667                    return blockList.get(r).getPermissiveWorking();
1668                default:
1669                    return "";
1670            }
1671        }
1672
1673        @Override
1674        public Class<?> getColumnClass(int c) {
1675            if (c == PERMISSIVE_COLUMN) {
1676                return Boolean.class;
1677            }
1678            return super.getColumnClass(c);
1679        }
1680
1681        @Override
1682        public String getColumnName(int col) {
1683            switch (col) {
1684                case SPEED_COLUMN:
1685                    return Bundle.getMessage("ColumnSpeed");  // NOI18N
1686                case PERMISSIVE_COLUMN:
1687                    return Bundle.getMessage("ColumnPermissive");  // NOI18N
1688                default:
1689                    // fall out
1690                    break;
1691            }
1692            return super.getColumnName(col);
1693        }
1694
1695        @Override
1696        public void setValueAt(Object type, int r, int c) {
1697            List<ManualBlockList> blockList;
1698            if (showAll) {
1699                blockList = _manualBlockList;
1700            } else {
1701                blockList = _includedManualBlockList;
1702            }
1703            switch (c) {
1704                case INCLUDE_COLUMN:
1705                    blockList.get(r).setIncluded((Boolean) type);
1706                    break;
1707                case STATE_COLUMN:
1708                    log.debug("State = {}", type);  // NOI18N
1709                    blockList.get(r).setSetToState((String) type);
1710                    break;
1711                default:
1712                    break;
1713            }
1714        }
1715
1716        /**
1717         * Provide a static JComboBox element to display inside the JPanel
1718         * CellEditor. When not yet present, create, store and return a new one.
1719         *
1720         * @param row Index number (in TableDataModel)
1721         * @return A combobox containing the valid aspect names for this mast
1722         */
1723        @Override
1724        JComboBox<String> getStateEditorBox(int row) {
1725            // create dummy comboBox, override in extended classes for each bean
1726            JComboBox<String> editCombo = new JComboBox<>();
1727            editCombo.addItem(SET_TO_UNOCCUPIED);
1728            editCombo.addItem(SET_TO_OCCUPIED);
1729            editCombo.addItem(SET_TO_ANY);
1730            return editCombo;
1731        }
1732
1733    }
1734
1735    /**
1736     * TableModel for selecting SML control Turnouts and Turnout Set To State.
1737     */
1738    class TurnoutModel extends TableModel {
1739
1740        TurnoutModel() {
1741            jmri.InstanceManager.turnoutManagerInstance().addPropertyChangeListener(this);
1742        }
1743
1744        @Override
1745        public int getRowCount() {
1746            if (showAll) {
1747                return _manualTurnoutList.size();
1748            } else {
1749                return _includedManualTurnoutList.size();
1750            }
1751        }
1752
1753        @Override
1754        public Object getValueAt(int r, int c) {
1755            List<ManualTurnoutList> turnoutList;
1756            if (showAll) {
1757                turnoutList = _manualTurnoutList;
1758            } else {
1759                turnoutList = _includedManualTurnoutList;
1760            }
1761            // some error checking
1762            if (r >= turnoutList.size()) {
1763                log.debug("row index is greater than turnout list");  // NOI18N
1764                return "error";  // NOI18N
1765            }
1766            switch (c) {
1767                case INCLUDE_COLUMN:
1768                    return turnoutList.get(r).isIncluded();
1769                case SNAME_COLUMN:
1770                    return turnoutList.get(r).getSysName();
1771                case UNAME_COLUMN:
1772                    return turnoutList.get(r).getUserName();
1773                case STATE_COLUMN:
1774                    // initial answer is 'Thrown', never null or empty
1775                    return turnoutList.get(r).getSetToState();
1776                default:
1777                    return null;
1778            }
1779        }
1780
1781        @Override
1782        public void setValueAt(Object type, int r, int c) {
1783            List<ManualTurnoutList> turnoutList;
1784            if (showAll) {
1785                turnoutList = _manualTurnoutList;
1786            } else {
1787                turnoutList = _includedManualTurnoutList;
1788            }
1789            switch (c) {
1790                case INCLUDE_COLUMN:
1791                    turnoutList.get(r).setIncluded((Boolean) type);
1792                    break;
1793                case STATE_COLUMN:
1794                    log.debug("State = {}", type);  // NOI18N
1795                    if (type != null) {
1796                        turnoutList.get(r).setSetToState((String) type);
1797                        fireTableRowsUpdated(r, r); // use new value
1798                    }
1799                    break;
1800                default:
1801                    break;
1802            }
1803        }
1804
1805        /**
1806         * Provide a static JComboBox element to display inside the JPanel
1807         * CellEditor. When not yet present, create, store and return a new one.
1808         *
1809         * @param row Index number (in TableDataModel)
1810         * @return A combobox containing the valid aspect names for this mast
1811         */
1812        @Override
1813        JComboBox<String> getStateEditorBox(int row) {
1814            // create dummy comboBox, override in extended classes for each bean
1815            JComboBox<String> editCombo = new JComboBox<>();
1816            editCombo.addItem(SET_TO_THROWN);
1817            editCombo.addItem(SET_TO_CLOSED);
1818            editCombo.addItem(SET_TO_ANY);
1819            return editCombo;
1820        }
1821
1822    }
1823
1824    /**
1825     * TableModel for selecting SML control Sensors and Sensor Set To State.
1826     */
1827    class SensorModel extends TableModel {
1828
1829        SensorModel() {
1830            InstanceManager.sensorManagerInstance().addPropertyChangeListener(this);
1831        }
1832
1833        @Override
1834        public int getRowCount() {
1835            if (showAll) {
1836                return _manualSensorList.size();
1837            } else {
1838                return _includedManualSensorList.size();
1839            }
1840        }
1841
1842        @Override
1843        public Object getValueAt(int r, int c) {
1844            List<ManualSensorList> sensorList;
1845            if (showAll) {
1846                sensorList = _manualSensorList;
1847            } else {
1848                sensorList = _includedManualSensorList;
1849            }
1850            // some error checking
1851            if (r >= sensorList.size()) {
1852                log.debug("row index is greater than sensor list");  // NOI18N
1853                return null;
1854            }
1855            switch (c) {
1856                case INCLUDE_COLUMN:
1857                    return sensorList.get(r).isIncluded();
1858                case SNAME_COLUMN:
1859                    return sensorList.get(r).getSysName();
1860                case UNAME_COLUMN:
1861                    return sensorList.get(r).getUserName();
1862                case STATE_COLUMN:
1863                    return sensorList.get(r).getSetToState();
1864                default:
1865                    return null;
1866            }
1867        }
1868
1869        @Override
1870        public void setValueAt(Object type, int r, int c) {
1871            List<ManualSensorList> sensorList;
1872            if (showAll) {
1873                sensorList = _manualSensorList;
1874            } else {
1875                sensorList = _includedManualSensorList;
1876            }
1877            switch (c) {
1878                case INCLUDE_COLUMN:
1879                    sensorList.get(r).setIncluded((Boolean) type);
1880                    break;
1881                case STATE_COLUMN:
1882                    sensorList.get(r).setSetToState((String) type);
1883                    break;
1884                default:
1885                    break;
1886            }
1887        }
1888
1889        /**
1890         * Provide a static JComboBox element to display inside the JPanel
1891         * CellEditor. When not yet present, create, store and return a new one.
1892         *
1893         * @param row Index number (in TableDataModel)
1894         * @return A combobox containing the valid aspect names for this mast
1895         */
1896        @Override
1897        JComboBox<String> getStateEditorBox(int row) {
1898            // create dummy comboBox, override in extended classes for each bean
1899            JComboBox<String> editCombo = new JComboBox<>();
1900            editCombo.addItem(SET_TO_INACTIVE);
1901            editCombo.addItem(SET_TO_ACTIVE);
1902            return editCombo;
1903        }
1904
1905    }
1906
1907    /**
1908     * Set up table for selecting Signal Masts and an Aspect on each mast
1909     * Updated for TableRowSorter
1910     */
1911    class SignalMastModel extends TableModel {
1912
1913        SignalMastModel() {
1914            jmri.InstanceManager.getDefault(jmri.SignalMastManager.class).addPropertyChangeListener(this);
1915        }
1916
1917        @Override
1918        public int getRowCount() {
1919            if (showAll) {
1920                return _manualSignalMastList.size();
1921            } else {
1922                return _includedManualSignalMastList.size();
1923            }
1924        }
1925
1926        @Override
1927        public Object getValueAt(int r, int c) { // get values from objects to display in table cells
1928            List<ManualSignalMastList> signalMastList;
1929            if (showAll) {
1930                signalMastList = _manualSignalMastList;
1931            } else {
1932                signalMastList = _includedManualSignalMastList;
1933            }
1934            // some error checking
1935            if (r >= signalMastList.size()) {
1936                log.debug("row index is greater than mast list");  // NOI18N
1937                return "error";  // NOI18N
1938            }
1939            switch (c) {
1940                case INCLUDE_COLUMN:
1941                    return signalMastList.get(r).isIncluded();
1942                case SNAME_COLUMN:
1943                    return signalMastList.get(r).getSysName();
1944                case UNAME_COLUMN:
1945                    return signalMastList.get(r).getUserName();
1946                case STATE_COLUMN:
1947                    try {
1948                        return signalMastList.get(r).getSetToState();
1949                    } catch (java.lang.NullPointerException e) {
1950                        //Aspect not set
1951                        log.debug("Aspect for mast {} not set", r);  // NOI18N
1952                        return Bundle.getMessage("BeanStateUnknown"); // use place holder string in table  // NOI18N
1953                    }
1954                default:
1955                    return null;
1956            }
1957        }
1958
1959        @Override
1960        public void setValueAt(Object type, int r, int c) { // store (new) choices in mast
1961            List<ManualSignalMastList> signalMastList;
1962            if (showAll) {
1963                signalMastList = _manualSignalMastList;
1964            } else {
1965                signalMastList = _includedManualSignalMastList;
1966            }
1967            switch (c) {
1968                case STATE_COLUMN:
1969                    if (type != null) {
1970                        //convertRowIndexToModel(row) not needed
1971                        log.debug("setValueAt (rowConverted={}; value={})", r, type);  // NOI18N
1972                        signalMastList.get(r).setSetToState((String) type);
1973                        fireTableRowsUpdated(r, r);
1974                    }
1975                    break;
1976                case INCLUDE_COLUMN:
1977                    signalMastList.get(r).setIncluded((Boolean) type);
1978                    break;
1979                default:
1980                    break;
1981            }
1982        }
1983
1984        @Override
1985        public Class<?> getColumnClass(int c) {
1986            if (c == STATE_COLUMN) {
1987                return RowComboBoxPanel.class; // Use a JPanel containing a custom State ComboBox
1988            }
1989            return super.getColumnClass(c);
1990        }
1991
1992        public String getValue(String name) { // called by Table Cell Renderer
1993            SignalMast sm = InstanceManager.getDefault(jmri.SignalMastManager.class).getBySystemName(name);
1994            if (sm != null) {
1995                return sm.getAspect(); // return _manualSignalMastList.get(sm).getSetToState(); // unspecific, have to translate sm to index in model
1996            } else {
1997                return null; // reporting "Unknown" seems useless for this application
1998            }
1999        }
2000
2001        @Override
2002        public String getColumnName(int col) {
2003            if (col == STATE_COLUMN) {
2004                return Bundle.getMessage("ColumnAspect"); // cf. line 1356 for general/other bean types  // NOI18N
2005            } else {
2006                return super.getColumnName(col);
2007            }
2008        }
2009
2010        /**
2011         * Customize the SignalMast State (Appearance) column to show an
2012         * appropriate ComboBox of available Aspects.
2013         *
2014         * @param table a JTable of Signal Masts
2015         */
2016        @Override
2017        protected void configStateColumn(JTable table) {
2018            // have the state column hold a JPanel with a JComboBox for Aspects
2019            // add extras, override BeanTableDataModel
2020            log.debug("Mast configStateColumn (I am {})", super.toString());  // NOI18N
2021            table.setDefaultEditor(RowComboBoxPanel.class, new AspectComboBoxPanel());
2022            table.setDefaultRenderer(RowComboBoxPanel.class, new AspectComboBoxPanel()); // use same class for the renderer
2023            // Set more things?
2024        }
2025
2026        /**
2027         * A row specific Aspect combobox cell editor/renderer.
2028         * <p>
2029         * This is a full version of the
2030         * {@link jmri.jmrit.beantable.RowComboBoxPanel} RowComboBox cell editor
2031         * class, including all hashtables and row specific comboBox choices.
2032         *
2033         * @see StateComboBoxPanel for a lightweight application when all that's
2034         * needed are identical options for all rows in a colomn.
2035         */
2036        public class AspectComboBoxPanel extends RowComboBoxPanel {
2037
2038            @Override
2039            protected final void eventEditorMousePressed() {
2040                this.editor.add(getEditorBox(table.convertRowIndexToModel(this.currentRow))); // add eb to JPanel
2041                this.editor.revalidate();
2042                SwingUtilities.invokeLater(this.comboBoxFocusRequester);
2043                log.debug("eventEditorMousePressed in row: {}; me = {})", this.currentRow, this.toString());  // NOI18N
2044            }
2045
2046            /**
2047             * Call method {@link #getAspectEditorBox(int)} in the surrounding
2048             * method for the SignalMastTable
2049             *
2050             * @param row Index of the row clicked in the table
2051             * @return an appropriate combobox for this signal mast
2052             */
2053            @Override
2054            protected JComboBox<String> getEditorBox(int row) {
2055                return getAspectEditorBox(row);
2056            }
2057        }
2058
2059        // Methods to display STATE_COLUMN (aspect) ComboBox in the Signal Mast Manual Table
2060        // All row values are in terms of the Model, not the Table as displayed.
2061
2062        /**
2063         * Provide a JComboBox element to display inside the JPanel CellEditor.
2064         * When not yet present, create, store and return a new one.
2065         *
2066         * @param row Index number (in TableDataModel)
2067         * @return A combobox containing the valid aspect names for this mast
2068         */
2069        JComboBox<String> getAspectEditorBox(int row) {
2070            JComboBox<String> editCombo = editorMap.get(this.getValueAt(row, SNAME_COLUMN));
2071            if (editCombo == null) {
2072                // create a new one with correct aspects
2073                editCombo = new JComboBox<>(getAspectVector(row)); // show it
2074                editorMap.put(this.getValueAt(row, SNAME_COLUMN), editCombo); // and store it
2075            }
2076            return editCombo;
2077        }
2078
2079        // Hashtables for Editors; none used for Renderers
2080        Hashtable<Object, JComboBox<String>> editorMap = new Hashtable<>();
2081
2082        /**
2083         * Holds a Hashtable of valid aspects per signal mast used by
2084         * getAspectEditorBox()
2085         *
2086         * @param row Index number (in TableDataModel)
2087         * @return The Vector of valid aspect names for this mast to show in the
2088         *         JComboBox
2089         */
2090        Vector<String> getAspectVector(int row) {
2091            Vector<String> comboaspects = boxMap.get(this.getValueAt(row, SNAME_COLUMN));
2092            if (comboaspects == null) {
2093                // create a new one with correct aspects
2094                SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class)
2095                    .getSignalMast((String) this.getValueAt(row, SNAME_COLUMN));
2096                if (mast!=null) {
2097                    comboaspects = mast.getValidAspects();
2098                    boxMap.put(this.getValueAt(row, SNAME_COLUMN), comboaspects); // and store it
2099                }
2100            }
2101            return comboaspects;
2102        }
2103
2104        private final Hashtable<Object, Vector<String>> boxMap = new Hashtable<>();
2105
2106        // end of methods to display STATE_COLUMN (Aspect) ComboBox
2107
2108    }
2109
2110    /**
2111     * A series of autoTableModels to display - but not edit - configurations on
2112     * the Edit SML Tabs that are autogenerated from layout Editor information.
2113     */
2114    abstract class AutoTableModel extends AbstractTableModel implements PropertyChangeListener {
2115
2116        AutoTableModel() {
2117            smlValid();
2118        }
2119
2120        final void smlValid() {
2121            if (sml != null) {
2122                sml.addPropertyChangeListener(this);
2123            }
2124        }
2125
2126        @Override
2127        public Class<?> getColumnClass(int c) {
2128            return String.class;
2129        }
2130
2131        /**
2132         * Remove references to and from this object, so that it can eventually
2133         * be garbage-collected.
2134         */
2135        public void dispose() {
2136            jmri.InstanceManager.turnoutManagerInstance().removePropertyChangeListener(this);
2137        }
2138
2139        @Override
2140        public String getColumnName(int col) {
2141            switch (col) {
2142                case SNAME_COLUMN:
2143                    return Bundle.getMessage("ColumnSystemName");  // NOI18N
2144                case UNAME_COLUMN:
2145                    return Bundle.getMessage("ColumnUserName");  // NOI18N
2146                case STATE_COLUMN:
2147                    return Bundle.getMessage("ColumnAspect"); // pick up via SignallingBundle as it is a different "State" label than non-signal tables  // NOI18N
2148
2149                default:
2150                    return "unknown";  // NOI18N
2151            }
2152        }
2153
2154        @Override
2155        public int getColumnCount() {
2156            return 3;
2157        }
2158
2159        @Override
2160        public boolean isCellEditable(int r, int c) {
2161            return false;
2162        }
2163
2164        public static final int SNAME_COLUMN = 0;
2165        public static final int UNAME_COLUMN = 1;
2166        public static final int STATE_COLUMN = 2;
2167    }
2168
2169    /**
2170     * TableModel to display - but not edit - Auto Layout Blocks on the Edit SML
2171     * Blocks Tab.
2172     */
2173    class AutoBlockModel extends AutoTableModel {
2174
2175        AutoBlockModel() {
2176            if (sml != null) {
2177                sml.addPropertyChangeListener(this);
2178            }
2179        }
2180
2181        static final int SPEED_COLUMN = 3;
2182        static final int PERMISSIVE_COLUMN = 4;
2183
2184        @Override
2185        public int getColumnCount() {
2186            return 5;
2187        }
2188
2189        @Override
2190        public String getColumnName(int col) {
2191            switch (col) {
2192                case SPEED_COLUMN:
2193                    return Bundle.getMessage("ColumnSpeed");  // NOI18N
2194                case PERMISSIVE_COLUMN:
2195                    return Bundle.getMessage("ColumnPermissive");  // NOI18N
2196                default:
2197                    // fall out
2198                    break;
2199            }
2200            return super.getColumnName(col);
2201        }
2202
2203        @Override
2204        public void propertyChange(java.beans.PropertyChangeEvent e) {
2205            if (e.getPropertyName().equals("autoblocks")) {  // NOI18N
2206                // a new NamedBean is available in the manager
2207                initializeIncludedList();
2208                fireTableDataChanged();
2209            }
2210        }
2211
2212        @Override
2213        public Class<?> getColumnClass(int c) {
2214            if (c == PERMISSIVE_COLUMN) {
2215                return Boolean.class;
2216            }
2217            return super.getColumnClass(c);
2218        }
2219
2220        @Override
2221        public int getRowCount() {
2222            return _automaticBlockList.size();
2223        }
2224
2225        @Override
2226        public Object getValueAt(int r, int c) {
2227            switch (c) {
2228                case SNAME_COLUMN:
2229                    return _automaticBlockList.get(r).getSysName();
2230                case UNAME_COLUMN:
2231                    return _automaticBlockList.get(r).getUserName();
2232                case STATE_COLUMN:
2233                    return _automaticBlockList.get(r).getSetToState();
2234                case SPEED_COLUMN:
2235                    return _automaticBlockList.get(r).getBlockSpeed();
2236                case PERMISSIVE_COLUMN:
2237                    return _automaticBlockList.get(r).getPermissiveWorking();
2238                default:
2239                    return null;
2240            }
2241        }
2242
2243        @Override
2244        public void setValueAt(Object type, int r, int c) {
2245        }
2246
2247        @Override
2248        public boolean isCellEditable(int r, int c) {
2249            return false;
2250        }
2251    }
2252
2253    /**
2254     * TableModel to display - but not edit - Auto Turnouts on the Edit SML
2255     * Turnouts Tab.
2256     */
2257    class AutoTurnoutModel extends AutoTableModel {
2258
2259        AutoTurnoutModel() {
2260            super();
2261        }
2262
2263        @Override
2264        public int getRowCount() {
2265            return _automaticTurnoutList.size();
2266        }
2267
2268        @Override
2269        public void propertyChange(java.beans.PropertyChangeEvent e) {
2270            if (e.getPropertyName().equals("autoturnouts")) {  // NOI18N
2271                // a new NamedBean is available in the manager
2272                initializeIncludedList();
2273                fireTableDataChanged();
2274            }
2275        }
2276
2277        @Override
2278        public Object getValueAt(int r, int c) {
2279            switch (c) {
2280                case SNAME_COLUMN:
2281                    return _automaticTurnoutList.get(r).getSysName();
2282                case UNAME_COLUMN:
2283                    return _automaticTurnoutList.get(r).getUserName();
2284                case STATE_COLUMN:
2285                    return _automaticTurnoutList.get(r).getSetToState();
2286                default:
2287                    return null;
2288            }
2289        }
2290    }
2291
2292    /**
2293     * TableModel to display - but not edit - Auto Signal Masts on the Edit SML
2294     * Signal Masts Tab.
2295     */
2296    class AutoMastModel extends AutoTableModel {
2297
2298        AutoMastModel() {
2299            super();
2300        }
2301
2302        @Override
2303        public int getRowCount() {
2304            return _automaticSignalMastList.size();
2305        }
2306
2307        @Override
2308        public void propertyChange(java.beans.PropertyChangeEvent e) {
2309            if (e.getPropertyName().equals("automasts")) {  // NOI18N
2310                // a new NamedBean is available in the manager
2311                initializeIncludedList();
2312                fireTableDataChanged();
2313            }
2314        }
2315
2316        @Override
2317        public Object getValueAt(int r, int c) {
2318            switch (c) {
2319                case SNAME_COLUMN:
2320                    return _automaticSignalMastList.get(r).getSysName();
2321                case UNAME_COLUMN:
2322                    return _automaticSignalMastList.get(r).getUserName();
2323                case STATE_COLUMN:
2324                    return _automaticSignalMastList.get(r).getSetToState();
2325                default:
2326                    return null;
2327            }
2328        }
2329    }
2330
2331    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SignallingPanel.class);
2332
2333}