001package jmri.jmrit.conditional;
002
003import java.awt.BorderLayout;
004import java.awt.Color;
005import java.awt.Component;
006import java.awt.Container;
007import java.awt.GridBagConstraints;
008import java.awt.GridBagLayout;
009import java.awt.event.*;
010import java.util.ArrayList;
011import java.util.List;
012import java.util.TreeMap;
013import java.util.TreeSet;
014import javax.swing.*;
015import javax.swing.event.*;
016import javax.swing.tree.*;
017
018import org.apache.commons.lang3.StringUtils;
019
020import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
021
022import javax.swing.filechooser.FileNameExtensionFilter;
023
024import jmri.*;
025import jmri.Conditional.Operator;
026import jmri.implementation.DefaultConditional;
027import jmri.implementation.DefaultConditionalAction;
028import jmri.jmrit.beantable.LRouteTableAction;
029import jmri.jmrit.logix.OBlock;
030import jmri.jmrit.logix.Warrant;
031import jmri.script.swing.ScriptFileChooser;
032import jmri.swing.NamedBeanComboBox;
033import jmri.util.FileUtil;
034import jmri.util.JmriJFrame;
035import jmri.util.swing.JComboBoxUtil;
036import jmri.util.swing.JmriJOptionPane;
037
038/**
039 * A tree based editor for maintaining Logix Conditionals, State Variables and
040 * Actions.
041 * <p>
042 * The tree has 3 levels. The first level are the conditionals contained in the
043 * selected Logix. The second level contains the antecedent, logic type and
044 * trigger mode settings. The third level contains the detail Variable and
045 * Action lines.
046 * <p>
047 * Compare with the other Conditional Edit tool {@link ConditionalListEdit}
048 *
049 * @author Dave Sand copyright (c) 2017
050 */
051public class ConditionalTreeEdit extends ConditionalEditBase {
052
053    /**
054     * Create a new Conditional Tree View editor.
055     *
056     * @param sName The system name of the current Logix
057     */
058    public ConditionalTreeEdit(String sName) {
059        super(sName);
060        buildConditionalComponents();
061        buildActionComponents();
062        buildVariableComponents();
063        makeEditLogixWindow();
064        setFocusListeners();
065        setEditMode(false);
066    }
067
068    public ConditionalTreeEdit() {
069    }
070
071    JPanel _curDetailPanel = new JPanel();
072    JTextField _editLogixUserName = new JTextField(20);   // Logix User Name field
073    NamedBeanComboBox<?> _comboNameBox = null;
074
075
076    // ------------ Edit detail components ------------
077    JPanel _detailGrid = new JPanel();
078    JPanel _detailFooter = new JPanel();
079    JPanel _gridPanel;  // Child of _detailGrid, contains the current grid labels and fields
080    JTextField _editConditionalUserName;
081    JTextField _editAntecedent;
082    JComboBox<Conditional.AntecedentOperator> _editOperatorMode;
083    boolean _editActive = false;
084    JButton _cancelAction;
085    JButton _updateAction;
086    String _pickCommand = null;
087    Conditional.ItemType _pickItem = Conditional.ItemType.NONE;
088
089    // ------------ Tree variables ------------
090    JTree _cdlTree;
091    DefaultTreeModel _cdlModel;
092    DefaultMutableTreeNode _cdlRoot;
093    TreeSelectionListener _cdlListener;
094    TreePath _curTreePath = null;
095
096    // ------------ Tree components ------------
097    ConditionalTreeNode _cdlNode = null;
098    ConditionalTreeNode _varHead = null;
099    ConditionalTreeNode _varNode = null;
100    ConditionalTreeNode _actHead = null;
101    ConditionalTreeNode _actNode = null;
102    ConditionalTreeNode _leafNode = null;
103
104    // ------------ Current tree node variables ------------
105    ConditionalTreeNode _curNode = null;
106    String _curNodeName = null;
107    String _curNodeType = null;
108    String _curNodeText = null;
109    int _curNodeRow = -1;
110
111    // ------------ Button bar components ------------
112    JPanel _leftButtonBar;
113    JPanel _labelPanel;
114    JPanel _addButtonPanel;
115    JPanel _toggleButtonPanel;
116    JPanel _checkButtonPanel;
117    JPanel _moveButtonPanel;
118    JPanel _deleteButtonPanel;
119    JPanel _helpButtonPanel;
120
121    JLabel _conditionalLabel = new JLabel(Bundle.getMessage("LabelConditionalActions"));  // NOI18N
122    JLabel _antecedentLabel = new JLabel(Bundle.getMessage("LabelAntecedentActions"));  // NOI18N
123    JLabel _logicTypeLabel = new JLabel(Bundle.getMessage("LabelLogicTypeActions"));  // NOI18N
124    JLabel _triggerModeLabel = new JLabel(Bundle.getMessage("LabelTriggerModeActions"));  // NOI18N
125    JLabel _variablesLabel = new JLabel(Bundle.getMessage("LabelVariablesActions"));  // NOI18N
126    JLabel _variableLabel = new JLabel(Bundle.getMessage("LabelVariableActions"));  // NOI18N
127    JLabel _actionsLabel = new JLabel(Bundle.getMessage("LabelActionsActions"));  // NOI18N
128    JLabel _actionLabel = new JLabel(Bundle.getMessage("LabelActionActions"));  // NOI18N
129
130    // ------------ Current conditional components ------------
131    Conditional _curConditional;
132    List<ConditionalVariable> _variableList;   // Current Variable List
133    List<ConditionalAction> _actionList;       // Current Action List
134    ConditionalVariable _curVariable;               // Current Variable
135    ConditionalAction _curAction;                   // Current Action
136    Conditional.ItemType _curVariableItem = Conditional.ItemType.NONE;
137    Conditional.ItemType _curActionItem = Conditional.ItemType.NONE;
138    String _curConditionalName = "";
139    String _antecedent;
140    Conditional.AntecedentOperator _logicType;
141    boolean _triggerMode;
142    boolean _newActionItem = false;
143    boolean _newVariableItem = false;
144    TreeSet<String> _oldTargetNames = new TreeSet<>();
145
146    // ------------ Select Conditional Variables ------------
147    JComboBox<String> _selectLogixBox = new JComboBox<>();
148    JComboBox<String> _selectConditionalBox = new JComboBox<>();
149    TreeMap<String, String> _selectLogixMap = new TreeMap<>();
150    ArrayList<String> _selectConditionalList = new ArrayList<>();
151
152    // ------------ Components of Edit Variable pane ------------
153    JComboBox<Conditional.ItemType> _variableItemBox;
154    JComboBox<Conditional.Type> _variableStateBox;
155    JComboBox<String> _variableOperBox;
156    JCheckBox _variableNegated;
157    JCheckBox _variableTriggerActions;
158    JTextField _variableNameField;
159    JLabel _variableNameLabel = new JLabel(Bundle.getMessage("LabelItemName"));  // NOI18N
160    JComboBox<String> _variableCompareOpBox;
161    JComboBox<String> _variableSignalBox;
162    JComboBox<Conditional.Type> _variableCompareTypeBox;
163    JLabel _variableMemoryValueLabel = new JLabel("");
164    JTextField _variableData1Field;
165    JTextField _variableData2Field;
166
167    // ------------ Components of Edit Action pane ------------
168    JComboBox<Conditional.ItemType> _actionItemBox;
169    JComboBox<Conditional.Action> _actionTypeBox;
170    JLabel _actionTypeLabel = new JLabel("Type");  // NOI18N
171    JTextField _actionNameField;
172    JLabel _actionNameLabel = new JLabel("Name");  // NOI18N
173    JComboBox<String> _actionBox;
174    JLabel _actionBoxLabel = new JLabel("Box");  // NOI18N
175    JTextField _longActionString;
176    JLabel _longActionLabel = new JLabel("Long");  // NOI18N
177    JTextField _shortActionString;
178    JLabel _shortActionLabel = new JLabel("Short");  // NOI18N
179    JComboBox<String> _actionOptionBox;
180    JButton _actionSetButton;
181
182    // ============  Edit conditionals for the current Logix ============
183
184    /**
185     * Create the edit logix window.
186     * <p>
187     * The left side contains a tree structure containing the conditionals for
188     * the current Logix. The right side contains detail edit panes based on the
189     * current tree row selection.
190     */
191    void makeEditLogixWindow() {
192        _editLogixFrame = new JmriJFrame(Bundle.getMessage("TitleEditLogix"));  // NOI18N
193        _editLogixFrame.addHelpMenu(
194                "package.jmri.jmrit.conditional.ConditionalTreeEditor", true);  // NOI18N
195        Container contentPane = _editLogixFrame.getContentPane();
196        contentPane.setLayout(new BorderLayout());
197
198        // ------------ Header ------------
199        JPanel header = new JPanel();
200        JPanel logixNames = new JPanel();
201        logixNames.setLayout(new BoxLayout(logixNames, BoxLayout.X_AXIS));
202
203        JLabel systemNameLabel = new JLabel(Bundle.getMessage("ColumnSystemName") + ":");  // NOI18N
204        logixNames.add(systemNameLabel);
205        logixNames.add(Box.createHorizontalStrut(5));
206
207        JLabel fixedSystemName = new JLabel(_curLogix.getSystemName());
208        logixNames.add(fixedSystemName);
209        logixNames.add(Box.createHorizontalStrut(20));
210
211        JLabel userNameLabel = new JLabel(Bundle.getMessage("ColumnUserName") + ":");  // NOI18N
212        logixNames.add(userNameLabel);
213        logixNames.add(Box.createHorizontalStrut(5));
214
215        _editLogixUserName.setText(_curLogix.getUserName());
216        logixNames.add(_editLogixUserName);
217        _editLogixUserName.setToolTipText(Bundle.getMessage("LogixUserNameHint2"));  // NOI18N
218        _editLogixUserName.addActionListener(new ActionListener() {
219            @Override
220            public void actionPerformed(ActionEvent e) {
221                String uName = _editLogixUserName.getText().trim();
222                if (!(uName.equals(_curLogix.getUserName()))) {
223                    // user name has changed - check if already in use
224                    if (uName.length() > 0) {
225                        Logix p = _logixManager.getByUserName(uName);
226                        if (p != null) {
227                            // Logix with this user name already exists
228                            log.error("Failure to update Logix with Duplicate User Name: {}", uName); // NOI18N
229                            JmriJOptionPane.showMessageDialog(_editLogixFrame,
230                                    Bundle.getMessage("Error6"), Bundle.getMessage("ErrorTitle"), // NOI18N
231                                    JmriJOptionPane.ERROR_MESSAGE);
232                            return;
233                        }
234                    }
235                    // user name is unique, change it
236                    logixData.clear();
237                    logixData.put("chgUname", uName);  // NOI18N
238                    fireLogixEvent();
239                    _showReminder = true;
240                }
241            }
242        });
243
244        header.add(logixNames);
245        contentPane.add(header, BorderLayout.NORTH);
246
247        // ------------ body - tree (left side) ------------
248        JTree treeContent = buildConditionalTree();
249        JScrollPane treeScroll = new JScrollPane(treeContent);
250
251        // ------------ body - detail (right side) ------------
252        JPanel detailPane = new JPanel();
253        detailPane.setBorder(BorderFactory.createMatteBorder(0, 2, 0, 0, Color.DARK_GRAY));
254        detailPane.setLayout(new BoxLayout(detailPane, BoxLayout.Y_AXIS));
255
256        // ------------ Edit Detail Panel ------------
257        makeDetailGrid("EmptyGrid");  // NOI18N
258
259        JPanel panel = new JPanel();
260        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
261
262        _cancelAction = new JButton(Bundle.getMessage("ButtonCancel"));  // NOI18N
263        _cancelAction.setToolTipText(Bundle.getMessage("HintCancelButton"));  // NOI18N
264        panel.add(_cancelAction);
265        _cancelAction.addActionListener(new ActionListener() {
266            @Override
267            public void actionPerformed(ActionEvent e) {
268                cancelPressed();
269            }
270        });
271        panel.add(Box.createHorizontalStrut(10));
272
273        _updateAction = new JButton(Bundle.getMessage("ButtonUpdate"));  // NOI18N
274        _updateAction.setToolTipText(Bundle.getMessage("UpdateButtonHint"));  // NOI18N
275        panel.add(_updateAction);
276        _updateAction.addActionListener(new ActionListener() {
277            @Override
278            public void actionPerformed(ActionEvent e) {
279                updatePressed();
280            }
281        });
282        _detailFooter.add(panel);
283
284        JPanel detailEdit = new JPanel(new BorderLayout());
285        detailEdit.add(_detailGrid, BorderLayout.NORTH);
286        detailEdit.add(_detailFooter, BorderLayout.SOUTH);
287        detailPane.add(detailEdit);
288        _editLogixUserName.setEnabled(true);
289
290        JSplitPane bodyPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeScroll, detailPane);
291        bodyPane.setDividerSize(10);
292        bodyPane.setResizeWeight(.35);
293        bodyPane.setOneTouchExpandable(true);
294        contentPane.add(bodyPane);
295
296        // ------------ footer ------------
297        JPanel footer = new JPanel(new BorderLayout());
298        _labelPanel = new JPanel();
299        _labelPanel.add(_conditionalLabel);
300        _leftButtonBar = new JPanel();
301        _leftButtonBar.add(_labelPanel);
302
303        // ------------ Add Button ------------
304        JButton addButton = new JButton(Bundle.getMessage("ButtonAddText")); // NOI18N
305        addButton.setToolTipText(Bundle.getMessage("HintAddButton"));        // NOI18N
306        addButton.addActionListener(new ActionListener() {
307            @Override
308            public void actionPerformed(ActionEvent e) {
309                addPressed();
310            }
311        });
312        _addButtonPanel = new JPanel();
313        _addButtonPanel.add(addButton);
314        _leftButtonBar.add(_addButtonPanel);
315
316        // ------------ Help Button ------------
317        JButton helpButton = new JButton(Bundle.getMessage("ButtonHelp"));  // NOI18N
318        helpButton.setToolTipText(Bundle.getMessage("HintHelpButton"));     // NOI18N
319        helpButton.addActionListener(new ActionListener() {
320            @Override
321            public void actionPerformed(ActionEvent e) {
322                helpPressed();
323            }
324        });
325        _helpButtonPanel = new JPanel();
326        _helpButtonPanel.add(helpButton);
327        _helpButtonPanel.setVisible(false);
328        _leftButtonBar.add(_helpButtonPanel);
329
330        // ------------ Toggle Button ------------
331        JButton toggleButton = new JButton(Bundle.getMessage("ButtonToggle"));  // NOI18N
332        toggleButton.setToolTipText(Bundle.getMessage("HintToggleButton"));     // NOI18N
333        toggleButton.addActionListener(new ActionListener() {
334            @Override
335            public void actionPerformed(ActionEvent e) {
336                togglePressed();
337            }
338        });
339        _toggleButtonPanel = new JPanel();
340        _toggleButtonPanel.add(toggleButton);
341        _toggleButtonPanel.setVisible(false);
342        _leftButtonBar.add(_toggleButtonPanel);
343
344        // ------------ Check Button ------------
345        JButton checkButton = new JButton(Bundle.getMessage("ButtonCheck"));  // NOI18N
346        checkButton.setToolTipText(Bundle.getMessage("HintCheckButton"));     // NOI18N
347        checkButton.addActionListener(new ActionListener() {
348            @Override
349            public void actionPerformed(ActionEvent e) {
350                checkPressed();
351            }
352        });
353        _checkButtonPanel = new JPanel();
354        _checkButtonPanel.add(checkButton);
355        _checkButtonPanel.setVisible(true);
356        _leftButtonBar.add(_checkButtonPanel);
357
358        // ------------ Delete Button ------------
359        JButton deleteButton = new JButton(Bundle.getMessage("ButtonDelete")); // NOI18N
360        deleteButton.setToolTipText(Bundle.getMessage("HintDeleteButton"));    // NOI18N
361        deleteButton.addActionListener(new ActionListener() {
362            @Override
363            public void actionPerformed(ActionEvent e) {
364                deletePressed();
365            }
366        });
367        _deleteButtonPanel = new JPanel();
368        _deleteButtonPanel.add(deleteButton);
369        _deleteButtonPanel.setVisible(false);
370        _leftButtonBar.add(_deleteButtonPanel);
371
372        footer.add(_leftButtonBar, BorderLayout.WEST);
373        JPanel rightButtonBar = new JPanel();
374
375        // ------------ Move Buttons ------------
376        JLabel moveLabel = new JLabel(Bundle.getMessage("LabelMove"));      // NOI18N
377
378        JButton upButton = new JButton(Bundle.getMessage("ButtonUp"));      // NOI18N
379        upButton.setToolTipText(Bundle.getMessage("HintUpButton"));         // NOI18N
380        JButton downButton = new JButton(Bundle.getMessage("ButtonDown"));  // NOI18N
381        downButton.setToolTipText(Bundle.getMessage("HintDownButton"));     // NOI18N
382
383        upButton.addActionListener(new ActionListener() {
384            @Override
385            public void actionPerformed(ActionEvent e) {
386                downButton.setEnabled(false);
387                upButton.setEnabled(false);
388                upPressed();
389            }
390        });
391
392        downButton.addActionListener(new ActionListener() {
393            @Override
394            public void actionPerformed(ActionEvent e) {
395                upButton.setEnabled(false);
396                downButton.setEnabled(false);
397                downPressed();
398            }
399        });
400
401        _moveButtonPanel = new JPanel();
402        _moveButtonPanel.add(moveLabel);
403        _moveButtonPanel.add(upButton);
404        _moveButtonPanel.add(new JLabel("|"));
405        _moveButtonPanel.add(downButton);
406        _moveButtonPanel.setVisible(false);
407        _leftButtonBar.add(_moveButtonPanel);
408
409        // ------------ Done Button ------------
410        JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));  // NOI18N
411        doneButton.setToolTipText(Bundle.getMessage("HintDoneButton"));     // NOI18N
412        doneButton.addActionListener(new ActionListener() {
413            @Override
414            public void actionPerformed(ActionEvent e) {
415                donePressed();
416            }
417        });
418        JPanel doneButtonPanel = new JPanel();
419        doneButtonPanel.add(doneButton);
420        rightButtonBar.add(doneButtonPanel);
421
422        footer.add(rightButtonBar, BorderLayout.EAST);
423        contentPane.add(footer, BorderLayout.SOUTH);
424
425        _editLogixFrame.addWindowListener(new java.awt.event.WindowAdapter() {
426            @Override
427            public void windowClosing(java.awt.event.WindowEvent e) {
428                donePressed();
429            }
430        });
431        _editLogixFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
432        _editLogixFrame.pack();
433        _editLogixFrame.setVisible(true);
434    }
435
436    /**
437     * Initialize conditional components.
438     */
439    void buildConditionalComponents() {
440        _editConditionalUserName = new JTextField(20);
441        _editAntecedent = new JTextField(20);
442        _editOperatorMode = new JComboBox<>();
443        for (Conditional.AntecedentOperator operator : Conditional.AntecedentOperator.values()) {
444            _editOperatorMode.addItem(operator);
445        }
446    }
447
448    // ------------ Create Conditional GridBag panels ------------
449
450    /**
451     * Build new GridBag content. The grid panel is hidden, emptied, re-built and
452     * made visible.
453     *
454     * @param gridType The type of grid to create
455     */
456    void makeDetailGrid(String gridType) {
457        _detailGrid.setVisible(false);
458        _detailGrid.removeAll();
459        _detailFooter.setVisible(true);
460
461        _gridPanel = new JPanel(new GridBagLayout());
462        GridBagConstraints c = new GridBagConstraints();
463        c.gridwidth = 1;
464        c.gridheight = 1;
465        c.ipadx = 5;
466
467        switch (gridType) {
468            case "EmptyGrid":  // NOI18N
469                makeEmptyGrid(c);
470                _detailFooter.setVisible(false);
471                break;
472
473            // ------------ Conditional Edit Grids ------------
474            case "Conditional":  // NOI18N
475                makeConditionalGrid(c);
476                break;
477
478            case "Antecedent":  // NOI18N
479                makeAntecedentGrid(c);
480                break;
481
482            case "LogicType":  // NOI18N
483                makeLogicTypeGrid(c);
484                break;
485
486            // ------------ Variable Edit Grids ------------
487            case "EmptyVariable":  // NOI18N
488                makeEmptyVariableGrid(c);
489                break;
490
491            case "StandardVariable":  // NOI18N
492                makeStandardVariableGrid(c);
493                break;
494
495            case "SignalAspectVariable":  // NOI18N
496                makeSignalAspectVariableGrid(c);
497                break;
498
499            case "ConditionalVariable":  // NOI18N
500                makeConditionalVariableGrid(c);
501                break;
502
503            case "MemoryVariable":  // NOI18N
504                makeMemoryVariableGrid(c);
505                break;
506
507            case "FastClockVariable":  // NOI18N
508                makeFastClockVariableGrid(c);
509                break;
510
511            // ------------ Action Edit Grids ------------
512            case "EmptyAction":  // NOI18N
513                makeEmptyActionGrid(c);
514                break;
515
516            case "NameTypeAction":  // NOI18N
517                makeNameTypeActionGrid(c, false);   // Skip change/trigger row
518                break;
519
520            case "NameTypeActionFinal":  // NOI18N
521                makeNameTypeActionGrid(c, true);    // Include change/trigger row
522                break;
523
524            case "TypeAction":  // NOI18N
525                makeTypeActionGrid(c, false);       // Skip change/trigger row
526                break;
527
528            case "TypeActionFinal":  // NOI18N
529                makeTypeActionGrid(c, true);        // Include change/trigger row
530                break;
531
532            case "TypeShortAction":  // NOI18N
533                makeTypeShortActionGrid(c);
534                break;
535
536            case "StandardAction":  // NOI18N
537                makeStandardActionGrid(c, true);    // Include change/trigger row
538                break;
539
540            case "ShortFieldAction":  // NOI18N
541                makeShortFieldActionGrid(c, true);  // Include Action Box
542                break;
543
544            case "ShortFieldNoBoxAction":  // NOI18N
545                makeShortFieldActionGrid(c, false); // Skip Action Box
546                break;
547
548            case "FileAction":  // NOI18N
549                makeFileActionGrid(c);
550                break;
551
552            default:
553                log.warn("Invalid grid type: '{}'", gridType);  // NOI18N
554                makeEmptyGrid(c);
555        }
556
557        _detailGrid.add(_gridPanel);
558        _detailGrid.setVisible(true);
559    }
560
561    /**
562     * This grid is used when there are no edit grids required.
563     *
564     * @param c The constraints object used for the grid construction
565     */
566    void makeEmptyGrid(GridBagConstraints c) {
567        // Variable type box
568        c.gridy = 0;
569        c.gridx = 0;
570        c.anchor = java.awt.GridBagConstraints.CENTER;
571        JLabel row0Label = new JLabel("This page is intentionally blank");  // NOI18N
572        _gridPanel.add(row0Label, c);
573    }
574
575    /**
576     * This grid is used to edit the Conditional User Name.
577     *
578     * @param c The constraints object used for the grid construction
579     */
580    void makeConditionalGrid(GridBagConstraints c) {
581        c.gridy = 0;
582        c.gridx = 0;
583        c.anchor = java.awt.GridBagConstraints.EAST;
584        JLabel row0Label = new JLabel(Bundle.getMessage("ConditionalUserName"));  // NOI18N
585        row0Label.setToolTipText(Bundle.getMessage("ConditionalUserNameHint"));  // NOI18N
586        _gridPanel.add(row0Label, c);
587        c.gridx = 1;
588        c.anchor = java.awt.GridBagConstraints.WEST;
589        _gridPanel.add(_editConditionalUserName, c);
590    }
591
592    /**
593     * This grid is used to edit the Antecedent when the Logic Type is Mixed.
594     *
595     * @param c The constraints object used for the grid construction
596     */
597    void makeAntecedentGrid(GridBagConstraints c) {
598        c.gridy = 0;
599        c.gridx = 0;
600        c.anchor = java.awt.GridBagConstraints.EAST;
601        JLabel row0Label = new JLabel(Bundle.getMessage("LabelAntecedentHeader"));  // NOI18N
602        row0Label.setToolTipText(Bundle.getMessage("LabelAntecedentHint"));  // NOI18N
603        _gridPanel.add(row0Label, c);
604        c.gridx = 1;
605        c.anchor = java.awt.GridBagConstraints.WEST;
606        _gridPanel.add(_editAntecedent, c);
607    }
608
609    /**
610     * This grid is used to edit the Logic Type.
611     *
612     * @param c The constraints object used for the grid construction
613     */
614    void makeLogicTypeGrid(GridBagConstraints c) {
615        c.gridy = 0;
616        c.gridx = 0;
617        c.anchor = java.awt.GridBagConstraints.EAST;
618        JLabel row0Label = new JLabel(Bundle.getMessage("LabelLogicType"));  // NOI18N
619        row0Label.setToolTipText(Bundle.getMessage("TypeLogicHint"));  // NOI18N
620        _gridPanel.add(row0Label, c);
621        c.gridx = 1;
622        c.anchor = java.awt.GridBagConstraints.WEST;
623        _gridPanel.add(_editOperatorMode, c);
624    }
625
626    // ------------ Process button bar and tree events ------------
627
628    /**
629     * Add new items: Conditionals, Variables or Actions.
630     */
631    void addPressed() {
632        if (_curNode == null) {
633            // New conditional with no prior selection
634            _curNodeType = "Conditional";  // NOI18N
635        }
636
637        switch (_curNodeType) {
638            case "Conditional":     // NOI18N
639                // make system name for new conditional
640                int num = _curLogix.getNumConditionals() + 1;
641                _curConditional = null;
642                String cName = null;
643                while (_curConditional == null) {
644                    cName = _curLogix.getSystemName() + "C" + Integer.toString(num);
645                    _curConditional = _conditionalManager.createNewConditional(cName, "");
646                    num++;
647                    if (num == 1000) {
648                        break;
649                    }
650                }
651                if (_curConditional == null) {
652                    // should never get here unless there is an assignment conflict
653                    log.error("Failure to create Conditional with System Name: {}", cName);  // NOI18N
654                    return;
655                }
656                // add to Logix at the end of the calculate order
657                _curLogix.addConditional(cName, -1);
658                _actionList = new ArrayList<>();
659                _variableList = new ArrayList<>();
660                _curConditional.setAction(_actionList);
661                _curConditional.setStateVariables(_variableList);
662                _showReminder = true;
663
664                // Build tree components
665                Conditional curConditional = _curLogix.getConditional(cName);
666                _curNode = new ConditionalTreeNode(buildNodeText("Conditional", curConditional, 0), "Conditional", cName, // NOI18N
667                        _curLogix.getNumConditionals() - 1);
668                _cdlRoot.add(_curNode);
669                _leafNode = new ConditionalTreeNode(buildNodeText("Antecedent", curConditional, 0), "Antecedent", cName, 0);   // NOI18N
670                _curNode.add(_leafNode);
671                _varHead = new ConditionalTreeNode(buildNodeText("Variables", curConditional, 0), "Variables", cName, 0);     // NOI18N
672                _curNode.add(_varHead);
673                _leafNode = new ConditionalTreeNode(buildNodeText("LogicType", curConditional, 0), "LogicType", cName, 0);      // NOI18N
674                _curNode.add(_leafNode);
675                _triggerMode = curConditional.getTriggerOnChange();
676                _leafNode = new ConditionalTreeNode(buildNodeText("TriggerMode", curConditional, 0), "TriggerMode", cName, 0);      // NOI18N
677                _curNode.add(_leafNode);
678                _actHead = new ConditionalTreeNode(buildNodeText("Actions", curConditional, 0), "Actions", cName, 0);      // NOI18N
679                _curNode.add(_actHead);
680                _cdlModel.nodeStructureChanged(_cdlRoot);
681
682                // Switch to new node
683                _cdlTree.setSelectionPath(new TreePath(_curNode.getPath()));
684                break;
685
686            case "Variables":    // NOI18N
687                newVariable();
688                break;
689
690            case "Variable":     // NOI18N
691                newVariable();
692                break;
693
694            case "Actions":    // NOI18N
695                newAction();
696                break;
697
698            case "Action":     // NOI18N
699                newAction();
700                break;
701
702            default:
703                log.error("Add called for unsupported node type: '{}'", _curNodeType);  // NOI18N
704        }
705    }
706
707    /**
708     * Create a new variable Can be invoked by a Variables or Variable node.
709     */
710    void newVariable() {
711        if (LRouteTableAction.getLogixInitializer().equals(_curLogix.getSystemName())) {
712           JmriJOptionPane.showMessageDialog(_editLogixFrame,
713                    Bundle.getMessage("Error49"), Bundle.getMessage("ErrorTitle"), // NOI18N
714                    JmriJOptionPane.ERROR_MESSAGE);
715            return;
716        }
717
718        cancelPressed();    // Make sure that there are no active edit sessions
719        _showReminder = true;
720        _curVariableItem = Conditional.ItemType.NONE;
721        ConditionalVariable variable = new ConditionalVariable();
722        _variableList.add(variable);
723        _newVariableItem = true;
724        setMoveButtons();   // Buttons will be disabled
725
726        int size = _variableList.size();
727        _curVariable = _variableList.get(size - 1);
728        // default of operator for postion 0 (row 1) is Conditional.OPERATOR_NONE
729        if (size > 1) {
730            if (_logicType == Conditional.AntecedentOperator.ALL_OR) {
731                _curVariable.setOpern(Conditional.Operator.OR);
732            } else {
733                _curVariable.setOpern(Conditional.Operator.AND);
734            }
735        }
736        appendToAntecedent();
737        size--;
738
739        // Update tree structure
740        if (_curNodeType.equals("Variables")) {  // NOI18N
741            _varHead = _curNode;
742        } else {
743            _varHead = (ConditionalTreeNode) _curNode.getParent();
744        }
745        _leafNode = new ConditionalTreeNode(buildNodeText("Variable", _curVariable, size), "Variable", _curNodeName, size);  // NOI18N
746        _varHead.add(_leafNode);
747        _cdlModel.nodeStructureChanged(_curNode);
748        _varHead.setRow(size + 1);
749        _cdlModel.nodeStructureChanged(_varHead);
750
751        // Switch to new node
752        ConditionalTreeNode tempNode = (ConditionalTreeNode) _varHead.getLastChild();
753        TreePath newPath = new TreePath(tempNode.getPath());
754        _cdlTree.setSelectionPath(newPath);
755        _cdlTree.expandPath(newPath);
756    }
757
758    /**
759     * Create a new action Can be invoked by a Actions or Action node.
760     */
761    void newAction() {
762        cancelPressed();    // Make sure that there are no active edit sessions
763        _showReminder = true;
764        _curActionItem = Conditional.ItemType.NONE;
765        ConditionalAction action = new DefaultConditionalAction();
766        _actionList.add(action);
767        _newActionItem = true;
768        setMoveButtons();   // Buttons will be disabled
769
770        int size = _actionList.size();
771        _curAction = _actionList.get(size - 1);
772        size--;
773
774        // Update tree structure
775        if (_curNodeType.equals("Actions")) {  // NOI18N
776            _actHead = _curNode;
777        } else {
778            _actHead = (ConditionalTreeNode) _curNode.getParent();
779        }
780        _leafNode = new ConditionalTreeNode(buildNodeText("Action", _curAction, size), "Action", _curNodeName, size);  // NOI18N
781        _actHead.add(_leafNode);
782        _cdlModel.nodeStructureChanged(_curNode);
783        _actHead.setRow(size + 1);
784        _cdlModel.nodeStructureChanged(_actHead);
785
786        // Switch to new node
787        ConditionalTreeNode tempNode = (ConditionalTreeNode) _actHead.getLastChild();
788        TreePath newPath = new TreePath(tempNode.getPath());
789        _cdlTree.setSelectionPath(newPath);
790        _cdlTree.expandPath(newPath);
791    }
792
793    /**
794     * Set up the edit environment for the selected node Called from
795     * {@link #treeRowSelected}. This takes the place of an actual button.
796     */
797    void editPressed() {
798        switch (_curNodeType) {
799            case "Conditional":     // NOI18N
800                _editConditionalUserName.setText(_curConditional.getUserName());
801                makeDetailGrid("Conditional");  // NOI18N
802                break;
803
804            case "Antecedent":      // NOI18N
805                Conditional.AntecedentOperator chkLogicType = _curConditional.getLogicType();
806                if (chkLogicType != Conditional.AntecedentOperator.MIXED) {
807                    makeDetailGrid("EmptyGrid");  // NOI18N
808                    return;
809                }
810                _labelPanel.add(_antecedentLabel);
811                _helpButtonPanel.setVisible(true);
812                _editAntecedent.setText(translateAntecedent(_curConditional.getAntecedentExpression(), false));
813                makeDetailGrid("Antecedent");  // NOI18N
814                break;
815
816            case "LogicType":       // NOI18N
817                Conditional.AntecedentOperator curLogicType = _curConditional.getLogicType();
818                _editOperatorMode.setSelectedItem(curLogicType);
819                makeDetailGrid("LogicType");  // NOI18N
820                break;
821
822            case "Variable":     // NOI18N
823                _labelPanel.add(_variableLabel);
824                _curVariable = _variableList.get(_curNodeRow);
825                _curVariableItem = _curVariable.getType().getItemType();
826                initializeStateVariables();
827                if (_logicType != Conditional.AntecedentOperator.MIXED) {
828                    setMoveButtons();
829                }
830                _oldTargetNames.clear();
831                loadReferenceNames(_variableList, _oldTargetNames);
832                break;
833
834            case "Action":     // NOI18N
835                _labelPanel.add(_actionLabel);
836                _curAction = _actionList.get(_curNodeRow);
837                _actionOptionBox.removeAllItems();
838                for (int i = 1; i <= Conditional.NUM_ACTION_OPTIONS; i++) {
839                    _actionOptionBox.addItem(DefaultConditionalAction.getOptionString(i, _triggerMode));
840                }
841                _curActionItem = _curAction.getType().getItemType();
842                initializeActionVariables();
843                setMoveButtons();
844                break;
845
846            default:
847                log.error("Edit called for unsupported node type: '{}'", _curNodeType);  // NOI18N
848        }
849    }
850
851    /**
852     * Apply the updates to the current node.
853     */
854    void updatePressed() {
855        switch (_curNodeType) {
856            case "Conditional":     // NOI18N
857                userNameChanged(_editConditionalUserName.getText().trim());
858                break;
859
860            case "Antecedent":      // NOI18N
861                antecedentChanged(_editAntecedent.getText().trim());
862                break;
863
864            case "LogicType":       // NOI18N
865                logicTypeChanged(_editOperatorMode.getItemAt(_editOperatorMode.getSelectedIndex()));
866                break;
867
868            case "Variable":       // NOI18N
869                updateVariable();
870                break;
871
872            case "Action":         // NOI18N
873                updateAction();
874                break;
875
876            default:
877                log.warn("Invalid update button press");  // NOI18N
878        }
879        setEditMode(false);
880        _cdlTree.setSelectionPath(_curTreePath);
881        _cdlTree.grabFocus();
882    }
883
884    /**
885     * Change the conditional user name.
886     *
887     * @param newName The proposed new name
888     */
889    void userNameChanged(String newName) {
890        // Check if the User Name has been changed
891        if (!newName.equals(_curConditional.getUserName())) {
892            // user name has changed - check if already in use
893            if (!checkConditionalUserName(newName, _curLogix)) {
894                return;
895            }
896            // user name is unique or blank, change it
897            _curConditional.setUserName(newName);
898            _curNode.setText(buildNodeText("Conditional", _curConditional, 0));  // NOI18N
899            _cdlModel.nodeChanged(_curNode);
900
901            // Update any conditional references
902            ArrayList<String> refList = _conditionalManager.getWhereUsed(_curNodeName);
903            if (refList != null) {
904                for (String ref : refList) {
905                    Conditional cRef = _conditionalManager.getBySystemName(ref);
906                    if (cRef==null){
907                        log.error("Conditional :{}: not found while updating username",ref);
908                        continue;
909                        }
910                    List<ConditionalVariable> varList = cRef.getCopyOfStateVariables();
911                    int idx = 0;
912                    for (ConditionalVariable var : varList) {
913                        // Find the affected conditional variable
914                        if (var.getName().equals(_curNodeName)) {
915                            if (newName.length() > 0) {
916                                var.setGuiName(newName);
917                            } else {
918                                var.setGuiName(_curNodeName);
919                            }
920
921                            // Is the reference (ref) in the same Logix as the target (_curNodeName)
922                            // Skip any cross conditional references
923                            String varLogixName = _conditionalManager.getParentLogix(ref).getSystemName();
924                            String curLogixSName = _curLogix.getSystemName();
925                            if (varLogixName.equals(curLogixSName)) {
926                                // Yes, update the tree node
927                                int cdlCount = _cdlRoot.getChildCount();
928                                for (int j = 0; j < cdlCount; j++) {
929                                    // See if a conditional node contains a reference
930                                    ConditionalTreeNode cdlNode = (ConditionalTreeNode) _cdlRoot.getChildAt(j);
931                                    if (cdlNode.getName().equals(ref)) {
932                                        // The affected variable node will be down 2 levels
933                                        ConditionalTreeNode variables = (ConditionalTreeNode) cdlNode.getChildAt(1);
934                                        ConditionalTreeNode variable = (ConditionalTreeNode) variables.getChildAt(idx);
935                                        variable.setText(buildNodeText("Variable", var, idx));    // NOI18N
936                                        _cdlModel.nodeChanged(variable);
937
938                                    }
939                                }
940                            }
941                        }
942                        idx++;
943                    }
944                    // Commit the changes
945                    cRef.setStateVariables(varList);
946                    // Refresh the local copy in case cRef was a parallel copy
947                    _variableList = _curConditional.getCopyOfStateVariables();
948                }
949            }
950        }
951    }
952
953    /**
954     * Respond to a change of Logic Type in the dialog window by showing/hiding
955     * the _antecedentPanel when Mixed is selected.
956     *
957     * @param newType The selected logic type
958     */
959    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
960    void logicTypeChanged(Conditional.AntecedentOperator newType) {
961        if (_logicType == newType) {
962            return;
963        }
964
965        makeAntecedent();
966        Operator oper;
967        if (newType != Conditional.AntecedentOperator.MIXED) {
968            oper = Conditional.Operator.OR;
969            if (newType == Conditional.AntecedentOperator.ALL_AND) {
970                oper = Conditional.Operator.AND;
971            }
972
973            // Update the variable list and tree node entries
974            ConditionalTreeNode varHead = (ConditionalTreeNode) _curNode.getPreviousSibling();
975            for (int i = 1; i < _variableList.size(); i++) {
976                ConditionalVariable curVar = _variableList.get(i);
977                curVar.setOpern(oper);
978
979                ConditionalTreeNode varNode = (ConditionalTreeNode) varHead.getChildAt(i);
980                varNode.setText(buildNodeText("Variable", curVar, i));
981                _cdlModel.nodeChanged(varNode);
982            }
983        }
984
985        // update LogicType entry and tree node
986        _curConditional.setLogicType(newType, _antecedent); // non-localized string to store Conditional Antecedent
987        _logicType = newType;
988        _curNode.setText(buildNodeText("LogicType", _curConditional, 0));  // NOI18N
989        _cdlModel.nodeChanged(_curNode);
990
991        // update the variables list
992        _curConditional.setStateVariables(_variableList);
993
994        // Update antecedent node text
995        ConditionalTreeNode parentNode = (ConditionalTreeNode) _curNode.getParent();
996        ConditionalTreeNode antNode = (ConditionalTreeNode) parentNode.getFirstChild();
997        if (antNode.getType().equals("Antecedent")) {  // NOI18N
998            antNode.setText(buildNodeText("Antecedent", _curConditional, 0));  // NOI18N
999            _cdlModel.nodeChanged(antNode);
1000        } else {
1001            log.warn("Unable to find the antecedent node");  // NOI18N
1002        }
1003    }
1004
1005    /**
1006     * Update the antecedent.
1007     *
1008     * @param antecedentText the new antecedent
1009     */
1010    void antecedentChanged(String antecedentText) {
1011        if (validateAntecedent(antecedentText)) {
1012            _antecedent = translateAntecedent(antecedentText, true);
1013            _curConditional.setLogicType(_logicType, _antecedent);
1014            _curNode.setText(buildNodeText("Antecedent", _curConditional, 0));
1015            _cdlModel.nodeChanged(_curNode);
1016        }
1017    }
1018
1019    /**
1020     * Build the antecedent statement.
1021     */
1022    void makeAntecedent() {
1023        _antecedent = makeAntecedent(_variableList);
1024    }
1025
1026    /**
1027     * Add a R# to the antecedent statement.
1028     */
1029    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
1030    void appendToAntecedent() {
1031        _antecedent = appendToAntecedent(_logicType, _variableList.size(), _antecedent);
1032        _curConditional.setLogicType(_logicType, _antecedent);
1033
1034        // Update antecedent node text
1035        ConditionalTreeNode antNode;
1036        if (_curNodeType.equals("Variables")) {  // NOI18N
1037            antNode = (ConditionalTreeNode) _curNode.getPreviousSibling();
1038        } else {
1039            antNode = (ConditionalTreeNode) ((ConditionalTreeNode) _curNode.getParent()).getPreviousSibling();
1040        }
1041
1042        if (antNode.getType().equals("Antecedent")) {  // NOI18N
1043            antNode.setText(buildNodeText("Antecedent", _curConditional, 0));  // localized display text NOI18N
1044            _cdlModel.nodeChanged(antNode);
1045        } else {
1046            log.warn("Unable to find the antecedent node");  // NOI18N
1047        }
1048    }
1049
1050    /**
1051     * Check the antecedent and logic type.
1052     *
1053     * @param antecedentText The user supplied antecedent text
1054     * @return false if antecedent can't be validated
1055     */
1056    boolean validateAntecedent(String antecedentText) {
1057        return validateAntecedent(_logicType, antecedentText, _variableList, _curConditional);
1058    }
1059
1060    /**
1061     * Update the Actions trigger mode, adjust the Action descriptions.
1062     */
1063    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
1064    void togglePressed() {
1065        // Toggle the trigger mode
1066        _curLogix.deActivateLogix();
1067        _curConditional.setTriggerOnChange(!_curConditional.getTriggerOnChange());
1068        _triggerMode = _curConditional.getTriggerOnChange();
1069        _curLogix.activateLogix();
1070
1071        // Update node text
1072        _curNode.setText(buildNodeText("TriggerMode", _curConditional, 0));  // NOI18N
1073        _cdlModel.nodeChanged(_curNode);
1074
1075        // refresh the action list to get the updated action descriptions
1076        _actionList = _curConditional.getCopyOfActions();
1077        // get next sibling and update the children node text
1078        ConditionalTreeNode actionsNode = (ConditionalTreeNode) _curNode.getNextSibling();
1079        for (int i = 0; i < _actionList.size(); i++) {
1080            ConditionalAction action = _actionList.get(i);
1081            ConditionalTreeNode actNode = (ConditionalTreeNode) actionsNode.getChildAt(i);
1082            actNode.setText(action.description(_triggerMode));
1083            _cdlModel.nodeChanged(actNode);
1084        }
1085    }
1086
1087    /**
1088     * Refresh the Conditional or Variable state.
1089     */
1090    void checkPressed() {
1091        if (_curNodeType == null || _curNodeType.equals("Conditional")) {
1092            for (int i = 0; i < _cdlRoot.getChildCount(); i++) {
1093                ConditionalTreeNode cdlNode = (ConditionalTreeNode) _cdlRoot.getChildAt(i);
1094                Conditional cdl = _conditionalManager.getBySystemName(cdlNode.getName());
1095                cdlNode.setText(buildNodeText("Conditional", cdl, i));  // NOI18N
1096                _cdlModel.nodeChanged(cdlNode);
1097            }
1098            return;
1099        }
1100
1101        if (_curNodeType.equals("Variables")) {  // NOI18N
1102            for (int i = 0; i < _variableList.size(); i++) {
1103                ConditionalVariable variable = _variableList.get(i);
1104                ConditionalTreeNode varNode = (ConditionalTreeNode) _curNode.getChildAt(i);
1105                varNode.setText(buildNodeText("Variable", variable, i));  // NOI18N
1106                _cdlModel.nodeChanged(varNode);
1107            }
1108        }
1109    }
1110
1111    /**
1112     * Process the node delete request.
1113     */
1114    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
1115    void deletePressed() {
1116        TreePath parentPath;
1117        ConditionalTreeNode parentNode;
1118        TreeSet<String> oldTargetNames = new TreeSet<>();
1119        TreeSet<String> newTargetNames = new TreeSet<>();
1120        setEditMode(false);
1121
1122        // Conditional
1123        switch (_curNodeType) {
1124            case "Conditional":   // NOI18N
1125                loadReferenceNames(_variableList, oldTargetNames);
1126                // Delete the conditional.
1127                _curLogix.deActivateLogix();
1128                String[] msgs = _curLogix.deleteConditional(_curNodeName);
1129                _curLogix.activateLogix();
1130                if (msgs != null) {
1131                    // Unable to delete due to existing conditional references
1132                   JmriJOptionPane.showMessageDialog(_editLogixFrame,
1133                            Bundle.getMessage("Error11", (Object[]) msgs), // NOI18N
1134                            Bundle.getMessage("ErrorTitle"),
1135                            JmriJOptionPane.ERROR_MESSAGE);  // NOI18N
1136                    return;
1137                }
1138                updateWhereUsed(oldTargetNames, newTargetNames, _curNodeName);
1139                _showReminder = true;
1140
1141                // Update the tree
1142                _cdlRoot.remove(_curNodeRow);
1143                _cdlModel.nodeStructureChanged(_cdlRoot);
1144
1145                // Update the row numbers
1146                int childCount = _cdlRoot.getChildCount();
1147                for (int i = 0; i < childCount; i++) {
1148                    _curNode = (ConditionalTreeNode) _cdlRoot.getChildAt(i);
1149                    _curNode.setRow(i);
1150                    _cdlModel.nodeChanged(_curNode);
1151                }
1152
1153                if (_curLogix.getNumConditionals() < 1 && !_suppressReminder) {
1154                    // warning message - last Conditional deleted
1155                   JmriJOptionPane.showMessageDialog(_editLogixFrame,
1156                            Bundle.getMessage("Warn1"), Bundle.getMessage("WarningTitle"), // NOI18N
1157                            JmriJOptionPane.WARNING_MESSAGE);
1158                }
1159                setMoveButtons();
1160                break;
1161
1162            case "Variable":      // NOI18N
1163                loadReferenceNames(_variableList, oldTargetNames);
1164                if (_variableList.size() < 2 && !_suppressReminder) {
1165                    // warning message - last State Variable deleted
1166                    JmriJOptionPane.showMessageDialog(_editLogixFrame,
1167                            Bundle.getMessage("Warn3"), Bundle.getMessage("WarningTitle"), // NOI18N
1168                            JmriJOptionPane.WARNING_MESSAGE);
1169                }
1170
1171                // Adjust operator
1172                if (_curNodeRow == 0 && _variableList.size() > 1) {
1173                    _variableList.get(1).setOpern(Conditional.Operator.NONE);
1174                }
1175
1176                // Remove the row, update and refresh the Variable list, update references
1177                _variableList.remove(_curNodeRow);
1178                updateVariableList();
1179                loadReferenceNames(_variableList, newTargetNames);
1180                updateWhereUsed(oldTargetNames, newTargetNames, _curNodeName);
1181                _showReminder = true;
1182
1183                // Update the tree components
1184                parentPath = _curTreePath.getParentPath();
1185                parentNode = (ConditionalTreeNode) _curNode.getParent();
1186                parentNode.setRow(_variableList.size());
1187                _cdlModel.nodeChanged(parentNode);
1188
1189                // Update the antecedent
1190                _curNode = (ConditionalTreeNode) parentNode.getPreviousSibling();
1191                antecedentChanged("");
1192
1193                // Update the variable children
1194                parentNode.removeAllChildren();
1195                for (int v = 0; v < _variableList.size(); v++) {
1196                    ConditionalVariable variable = _variableList.get(v);
1197                    _leafNode = new ConditionalTreeNode(buildNodeText("Variable", variable, v), // NOI18N
1198                            "Variable", _curNodeName, v);  // NOI18N
1199                    parentNode.add(_leafNode);
1200                }
1201
1202                _curNode = null;
1203                _newVariableItem = false;
1204                cleanUpVariable();
1205
1206                _cdlModel.nodeStructureChanged(parentNode);
1207                _cdlTree.setSelectionPath(parentPath);
1208                break;
1209
1210            case "Action":        // NOI18N
1211                // Remove the row, update and refresh the Action list
1212                removeActionTimers();
1213                _actionList.remove(_curNodeRow);
1214                updateActionList();
1215                _showReminder = true;
1216
1217                // Update the tree components
1218                parentPath = _curTreePath.getParentPath();
1219                parentNode = (ConditionalTreeNode) _curNode.getParent();
1220                parentNode.setRow(_actionList.size());
1221                _cdlModel.nodeChanged(parentNode);
1222                parentNode.removeAllChildren();
1223                for (int a = 0; a < _actionList.size(); a++) {
1224                    ConditionalAction action = _actionList.get(a);
1225                    _leafNode = new ConditionalTreeNode(buildNodeText("Action", action, a), // NOI18N
1226                            "Action", _curNodeName, a);      // NOI18N
1227                    parentNode.add(_leafNode);
1228                }
1229
1230                _curNode = null;
1231                _newActionItem = false;
1232                cleanUpAction();
1233
1234                _cdlModel.nodeStructureChanged(parentNode);
1235                _cdlTree.setSelectionPath(parentPath);
1236                break;
1237
1238            default:
1239                log.error("Delete called for unsupported node type: '{}'", _curNodeType);  // NOI18N
1240        }
1241    }
1242
1243    /**
1244     * Move a conditional, variable or action row up 1 row.
1245     */
1246    void upPressed() {
1247        _showReminder = true;
1248
1249        switch (_curNodeType) {
1250            case "Conditional":         // NOI18N
1251                // Update Logix index
1252                _curLogix.deActivateLogix();
1253                _curLogix.swapConditional(_curNodeRow - 1, _curNodeRow);
1254                _curLogix.activateLogix();
1255                moveTreeNode("Up");     // NOI18N
1256                break;
1257
1258            case "Variable":            // NOI18N
1259                ConditionalVariable tempVar = _variableList.get(_curNodeRow);
1260                int newVarRow = _curNodeRow - 1;
1261                _variableList.set(_curNodeRow, _variableList.get(newVarRow));
1262                _variableList.set(newVarRow, tempVar);
1263                // Adjust operator
1264                if (newVarRow == 0) {
1265                    _variableList.get(newVarRow).setOpern(Conditional.Operator.NONE);
1266                    Operator newOper = (_logicType == Conditional.AntecedentOperator.ALL_AND)
1267                            ? Conditional.Operator.AND : Conditional.Operator.OR;
1268                    _variableList.get(_curNodeRow).setOpern(newOper);
1269                }
1270                updateVariableList();
1271                moveTreeNode("Up");     // NOI18N
1272                break;
1273
1274            case "Action":              // NOI18N
1275                ConditionalAction tempAct = _actionList.get(_curNodeRow);
1276                int newActRow = _curNodeRow - 1;
1277                _actionList.set(_curNodeRow, _actionList.get(newActRow));
1278                _actionList.set(newActRow, tempAct);
1279                removeActionTimers();
1280                updateActionList();
1281                moveTreeNode("Up");     // NOI18N
1282                break;
1283
1284            default:
1285                log.warn("Move Up called for unsupported node type: '{}'", _curNodeType);  // NOI18N
1286        }
1287    }
1288
1289    /**
1290     * Move a conditional, variable or action row down 1 row.
1291     */
1292    void downPressed() {
1293        _showReminder = true;
1294
1295        switch (_curNodeType) {
1296            case "Conditional":         // NOI18N
1297                _curLogix.deActivateLogix();
1298                _curLogix.swapConditional(_curNodeRow, _curNodeRow + 1);
1299                _curLogix.activateLogix();
1300                moveTreeNode("Down");   // NOI18N
1301                break;
1302
1303            case "Variable":            // NOI18N
1304                ConditionalVariable tempVar = _variableList.get(_curNodeRow);
1305                int newVarRow = _curNodeRow + 1;
1306                _variableList.set(_curNodeRow, _variableList.get(newVarRow));
1307                _variableList.set(newVarRow, tempVar);
1308                // Adjust operator
1309                if (_curNodeRow == 0) {
1310                    _variableList.get(_curNodeRow).setOpern(Conditional.Operator.NONE);
1311                    Operator newOper = (_logicType == Conditional.AntecedentOperator.ALL_AND)
1312                            ? Conditional.Operator.AND : Conditional.Operator.OR;
1313                    _variableList.get(newVarRow).setOpern(newOper);
1314                }
1315                updateVariableList();
1316                moveTreeNode("Down");   // NOI18N
1317                break;
1318
1319            case "Action":              // NOI18N
1320                ConditionalAction tempAct = _actionList.get(_curNodeRow);
1321                int newActRow = _curNodeRow + 1;
1322                _actionList.set(_curNodeRow, _actionList.get(newActRow));
1323                _actionList.set(newActRow, tempAct);
1324                removeActionTimers();
1325                updateActionList();
1326                moveTreeNode("Down");   // NOI18N
1327                break;
1328
1329            default:
1330                log.warn("Move Down called for unsupported node type: '{}'", _curNodeType);  // NOI18N
1331        }
1332    }
1333
1334    /**
1335     * Remove Action timers and listeners before Action list structure changes.
1336     * This relates to moving and deleting rows.  New actions at the end are not problem.
1337     * The issue is that the timer listeners are tied to the action row number.
1338     * This can result in orphan timers and listeners that keep running.
1339     * @since 4.11.2
1340     */
1341    void removeActionTimers() {
1342        // Use the real list, not a copy.
1343        DefaultConditional cdl = (DefaultConditional) _curConditional;
1344        for (ConditionalAction act : cdl.getActionList()) {
1345            if (act.getTimer() != null) {
1346                act.stopTimer();
1347                act.setTimer(null);
1348                act.setListener(null);
1349            }
1350        }
1351    }
1352
1353    /**
1354     * Move a tree node in response to a up or down request.
1355     *
1356     * @param direction The direction of movement, Up or Down
1357     */
1358    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
1359    void moveTreeNode(String direction) {
1360        // Update the node
1361        int oldRow = _curNodeRow;
1362        if (direction.equals("Up")) {    // NOI18N
1363            _curNodeRow -= 1;
1364        } else {
1365            _curNodeRow += 1;
1366        }
1367        _curNode.setRow(_curNodeRow);
1368        if (_curNodeType.equals("Variable")) {  // NOI18N
1369            _curNode.setText(buildNodeText("Variable", _variableList.get(_curNodeRow), _curNodeRow));   // NOI18N
1370        }
1371        _cdlModel.nodeChanged(_curNode);
1372
1373        // Update the sibling
1374        ConditionalTreeNode siblingNode;
1375        if (direction.equals("Up")) {    // NOI18N
1376            siblingNode = (ConditionalTreeNode) _curNode.getPreviousSibling();
1377            siblingNode.setRow(siblingNode.getRow() + 1);
1378        } else {
1379            siblingNode = (ConditionalTreeNode) _curNode.getNextSibling();
1380            siblingNode.setRow(siblingNode.getRow() - 1);
1381        }
1382        if (_curNodeType.equals("Variable")) {  // NOI18N
1383            siblingNode.setText(buildNodeText("Variable", _variableList.get(oldRow), oldRow));  // NOI18N
1384        }
1385        _cdlModel.nodeChanged(siblingNode);
1386
1387        // Update the tree
1388        if (_curNodeType.equals("Conditional")) {   // NOI18N
1389            _cdlRoot.insert(_curNode, _curNodeRow);
1390            _cdlModel.nodeStructureChanged(_cdlRoot);
1391        } else {
1392            ConditionalTreeNode parentNode = (ConditionalTreeNode) _curNode.getParent();
1393            parentNode.insert(_curNode, _curNodeRow);
1394            _cdlModel.nodeStructureChanged(parentNode);
1395        }
1396        _cdlTree.setSelectionPath(new TreePath(_curNode.getPath()));
1397        setMoveButtons();
1398    }
1399
1400    /**
1401     * Enable/Disable the Up and Down buttons based on the postion in the list.
1402     */
1403    void setMoveButtons() {
1404        if (_curNode == null) {
1405            return;
1406        }
1407
1408        Component[] compList = _moveButtonPanel.getComponents();
1409        JButton up = (JButton) compList[1];
1410        JButton down = (JButton) compList[3];
1411
1412        up.setEnabled(true);
1413        down.setEnabled(true);
1414
1415        int rows;
1416        if (_curNodeType.equals("Conditional")) {       // NOI18N
1417            rows = _curLogix.getNumConditionals();
1418        } else {
1419            ConditionalTreeNode parent = (ConditionalTreeNode) _curNode.getParent();
1420            rows = parent.getRow();
1421        }
1422
1423        if (_curNodeRow < 1) {
1424            up.setEnabled(false);
1425        }
1426        if (_curNodeRow >= rows - 1) {
1427            down.setEnabled(false);
1428        }
1429
1430        // Disable move buttons during Variable or Action add or edit processing, or nothing selected
1431        if ((_newVariableItem && _curNodeType.equals("Variable")) // NOI18N
1432                || (_newActionItem && _curNodeType.equals("Action"))
1433                || (_editActive)
1434                || (_cdlTree.getSelectionCount() == 0)) {  // NOI18N
1435            up.setEnabled(false);
1436            down.setEnabled(false);
1437        }
1438
1439        _moveButtonPanel.setVisible(true);
1440    }
1441
1442    /**
1443     * Respond to Help button press in the Edit Logix menu bar. Only visible when
1444     * using mixed mode and an antecedent node is selected.
1445     */
1446    void helpPressed() {
1447        JmriJOptionPane.showMessageDialog(null,
1448                new String[]{
1449                    Bundle.getMessage("ConditionalHelpText1"), // NOI18N
1450                    Bundle.getMessage("ConditionalHelpText2"), // NOI18N
1451                    Bundle.getMessage("ConditionalHelpText3"), // NOI18N
1452                    Bundle.getMessage("ConditionalHelpText4"), // NOI18N
1453                    Bundle.getMessage("ConditionalHelpText5"), // NOI18N
1454                    Bundle.getMessage("ConditionalHelpText6"), // NOI18N
1455                    Bundle.getMessage("ConditionalHelpText7") // NOI18N
1456                },
1457                Bundle.getMessage("MenuHelp"), JmriJOptionPane.INFORMATION_MESSAGE);  // NOI18N
1458    }
1459
1460    /**
1461     * Cancel the current node edit.
1462     */
1463    void cancelPressed() {
1464        switch (_curNodeType) {
1465            case "Variable":       // NOI18N
1466                cancelEditVariable();
1467                break;
1468
1469            case "Action":         // NOI18N
1470                cancelEditAction();
1471                break;
1472
1473            default:
1474                break;
1475        }
1476        makeDetailGrid("EmptyGrid");  // NOI18N
1477        setEditMode(false);
1478        _cdlTree.setSelectionPath(_curTreePath);
1479        _cdlTree.grabFocus();
1480    }
1481
1482    /**
1483     * Clean up, notify the parent Logix that edit session is done.
1484     */
1485    void donePressed() {
1486        showSaveReminder();
1487        if (_curNodeType != null) {
1488            switch (_curNodeType) {
1489                case "Variable":       // NOI18N
1490                    cancelEditVariable();
1491                    break;
1492
1493                case "Action":         // NOI18N
1494                    cancelEditAction();
1495                    break;
1496
1497                default:
1498                    break;
1499            }
1500        }
1501        closeSinglePanelPickList();
1502        if (_pickTables != null) {
1503            _pickTables.dispose();
1504            _pickTables = null;
1505        }
1506
1507        _editLogixFrame.setVisible(false);
1508        _editLogixFrame.dispose();
1509        _editLogixFrame = null;
1510
1511        logixData.clear();
1512        logixData.put("Finish", _curLogix.getSystemName());  // NOI18N
1513        fireLogixEvent();
1514    }
1515
1516    // ============  Tree Content and Navigation ============
1517
1518    /**
1519     * Create the conditional tree structure using the current Logix.
1520     *
1521     * @return _cdlTree The tree ddefinition with its content
1522     */
1523    JTree buildConditionalTree() {
1524        _cdlRoot = new DefaultMutableTreeNode("Root Node");      // NOI18N
1525        _cdlModel = new DefaultTreeModel(_cdlRoot);
1526        _cdlTree = new JTree(_cdlModel);
1527
1528        createConditionalContent();
1529
1530        // build the tree GUI
1531        _cdlTree.expandPath(new TreePath(_cdlRoot));
1532        _cdlTree.setRootVisible(false);
1533        _cdlTree.setShowsRootHandles(true);
1534        _cdlTree.setScrollsOnExpand(true);
1535        _cdlTree.setExpandsSelectedPaths(true);
1536        _cdlTree.getSelectionModel().setSelectionMode(DefaultTreeSelectionModel.SINGLE_TREE_SELECTION);
1537
1538        // tree listeners
1539        _cdlTree.addTreeSelectionListener(_cdlListener = new TreeSelectionListener() {
1540            @Override
1541            public void valueChanged(TreeSelectionEvent e) {
1542                if (_editActive) {
1543                    if (e.getNewLeadSelectionPath() != _curTreePath) {
1544                        _cdlTree.setSelectionPath(e.getOldLeadSelectionPath());
1545                        showNodeEditMessage();
1546                    }
1547                    return;
1548                }
1549
1550                _curTreePath = _cdlTree.getSelectionPath();
1551                if (_curTreePath != null) {
1552                    Object chkLast = _curTreePath.getLastPathComponent();
1553                    if (chkLast instanceof ConditionalTreeNode) {
1554                        treeRowSelected((ConditionalTreeNode) chkLast);
1555                    }
1556                }
1557            }
1558        });
1559
1560        _cdlTree.addTreeExpansionListener(new TreeExpansionListener() {
1561            @Override
1562            public void treeExpanded(TreeExpansionEvent e) {
1563                ConditionalTreeNode checkNode = (ConditionalTreeNode) e.getPath().getLastPathComponent();
1564                if (checkNode.getType().equals("Variables")) {  // NOI18N
1565                    // Include the field descriptions in the node name
1566                    checkNode.setText(buildNodeText("Variables", _curConditional, 1));  // NOI18N
1567                    _cdlModel.nodeChanged(checkNode);
1568                }
1569            }
1570
1571            @Override
1572            public void treeCollapsed(TreeExpansionEvent e) {
1573                ConditionalTreeNode checkNode = (ConditionalTreeNode) e.getPath().getLastPathComponent();
1574                if (checkNode.getType().equals("Variables")) {  // NOI18N
1575                    // Remove the field descriptions from the node name
1576                    checkNode.setText(buildNodeText("Variables", _curConditional, 0));  // NOI18N
1577                    _cdlModel.nodeChanged(checkNode);
1578                }
1579
1580                if (_cdlTree.getSelectionCount() == 0) {
1581                    makeDetailGrid("EmptyGrid");  // NOI18N
1582                }
1583            }
1584        });
1585
1586        return _cdlTree;
1587    }
1588
1589    /**
1590     * Create the tree content. Level 1 are the conditionals, Level 2 includes the
1591     * antecedent, logic type, trigger mode and parent nodes for Variables and
1592     * Actions, Level 3 contains the detail Variable and Action entries.
1593     */
1594    void createConditionalContent() {
1595        int numConditionals = _curLogix.getNumConditionals();
1596        for (int i = 0; i < numConditionals; i++) {
1597            String csName = _curLogix.getConditionalByNumberOrder(i);
1598            Conditional curConditional = _curLogix.getConditional(csName);
1599            _cdlNode = new ConditionalTreeNode(buildNodeText("Conditional", curConditional, 0), "Conditional", csName, i);    // NOI18N
1600            _cdlRoot.add(_cdlNode);
1601
1602            _leafNode = new ConditionalTreeNode(buildNodeText("Antecedent", curConditional, 0), "Antecedent", csName, 0);   // NOI18N
1603            _cdlNode.add(_leafNode);
1604
1605            _variableList = curConditional.getCopyOfStateVariables();
1606            int varCount = _variableList.size();
1607            _varHead = new ConditionalTreeNode(buildNodeText("Variables", _curConditional, 0), "Variables", csName, varCount);     // NOI18N
1608            _cdlNode.add(_varHead);
1609            for (int v = 0; v < _variableList.size(); v++) {
1610                ConditionalVariable variable = _variableList.get(v);
1611                _leafNode = new ConditionalTreeNode(buildNodeText("Variable", variable, v), "Variable", csName, v);
1612                _varHead.add(_leafNode);
1613            }
1614
1615            _leafNode = new ConditionalTreeNode(buildNodeText("LogicType", curConditional, 0), "LogicType", csName, 0);      // NOI18N
1616            _cdlNode.add(_leafNode);
1617
1618            boolean triggerMode = curConditional.getTriggerOnChange();
1619            _leafNode = new ConditionalTreeNode(buildNodeText("TriggerMode", curConditional, 0), "TriggerMode", csName, 0);      // NOI18N
1620            _cdlNode.add(_leafNode);
1621
1622            _actionList = curConditional.getCopyOfActions();
1623            int actCount = _actionList.size();
1624            _actHead = new ConditionalTreeNode("Actions", "Actions", csName, actCount);      // NOI18N
1625            _cdlNode.add(_actHead);
1626            for (int a = 0; a < _actionList.size(); a++) {
1627                ConditionalAction action = _actionList.get(a);
1628                _leafNode = new ConditionalTreeNode(action.description(triggerMode), "Action", csName, a);      // NOI18N
1629                _actHead.add(_leafNode);
1630            }
1631        }
1632    }
1633
1634    /**
1635     * Change the button row based on the currently selected node type. Invoke
1636     * edit where appropriate.
1637     *
1638     * @param selectedNode The node object
1639     */
1640    void treeRowSelected(ConditionalTreeNode selectedNode) {
1641        // Set the current node variables
1642        _curNode = selectedNode;
1643        _curNodeName = selectedNode.getName();
1644        _curNodeType = selectedNode.getType();
1645        _curNodeText = selectedNode.getText();
1646        _curNodeRow = selectedNode.getRow();
1647
1648        // Set the current conditional variables if different conditional
1649        if (!_curConditionalName.equals(_curNodeName)) {
1650            _curConditional = _conditionalManager.getConditional(_curNodeName);
1651            _antecedent = _curConditional.getAntecedentExpression();
1652            _logicType = _curConditional.getLogicType();
1653            _triggerMode = _curConditional.getTriggerOnChange();
1654            _variableList = _curConditional.getCopyOfStateVariables();
1655            _actionList = _curConditional.getCopyOfActions();
1656            _curConditionalName = _curNodeName;
1657        }
1658
1659        // Reset button bar
1660        _addButtonPanel.setVisible(false);
1661        _checkButtonPanel.setVisible(false);
1662        _toggleButtonPanel.setVisible(false);
1663        _moveButtonPanel.setVisible(false);
1664        _deleteButtonPanel.setVisible(false);
1665        _helpButtonPanel.setVisible(false);
1666
1667        _labelPanel.removeAll();
1668        switch (_curNodeType) {
1669            case "Conditional":     // NOI18N
1670                _labelPanel.add(_conditionalLabel);
1671                _addButtonPanel.setVisible(true);
1672                _checkButtonPanel.setVisible(true);
1673                _deleteButtonPanel.setVisible(true);
1674                setMoveButtons();
1675                editPressed();
1676                break;
1677
1678            case "Antecedent":      // NOI18N
1679                editPressed();
1680                break;
1681
1682            case "LogicType":       // NOI18N
1683                editPressed();
1684                break;
1685
1686            case "TriggerMode":     // NOI18N
1687                _labelPanel.add(_triggerModeLabel);
1688                _toggleButtonPanel.setVisible(true);
1689                makeDetailGrid("EmptyGrid");  // NOI18N
1690                break;
1691
1692            case "Variables":       // NOI18N
1693                _labelPanel.add(_variablesLabel);
1694                _addButtonPanel.setVisible(true);
1695                _checkButtonPanel.setVisible(true);
1696                makeDetailGrid("EmptyGrid");  // NOI18N
1697                break;
1698
1699            case "Variable":        // NOI18N
1700                _labelPanel.add(_variableLabel);
1701                _addButtonPanel.setVisible(true);
1702                _deleteButtonPanel.setVisible(true);
1703                if (_logicType != Conditional.AntecedentOperator.MIXED) {
1704                    setMoveButtons();
1705                }
1706                editPressed();
1707                break;
1708
1709            case "Actions":         // NOI18N
1710                _labelPanel.add(_actionsLabel);
1711                _addButtonPanel.setVisible(true);
1712                makeDetailGrid("EmptyGrid");  // NOI18N
1713                break;
1714
1715            case "Action":          // NOI18N
1716                _labelPanel.add(_actionLabel);
1717                _addButtonPanel.setVisible(true);
1718                _deleteButtonPanel.setVisible(true);
1719                setMoveButtons();
1720                editPressed();
1721                break;
1722
1723            default:
1724                log.warn("Should not be here");  // NOI18N
1725        }
1726    }
1727
1728    /**
1729     * Create the localized node text display strings based on node type.
1730     *
1731     * @param nodeType  The type of the node
1732     * @param component The conditional object or child object
1733     * @param idx       Optional index value
1734     * @return nodeText containing the text to display on the node
1735     */
1736    String buildNodeText(String nodeType, Object component, int idx) {
1737        Conditional cdl;
1738        ConditionalAction act;
1739        ConditionalVariable var;
1740
1741        switch (nodeType) {
1742            case "Conditional":  // NOI18N
1743                cdl = (Conditional) component;
1744                String cdlStatus = (cdl.getState() == Conditional.TRUE)
1745                        ? Bundle.getMessage("True") // NOI18N
1746                        : Bundle.getMessage("False");  // NOI18N
1747                String cdlNames = cdl.getSystemName() + " -- " + cdl.getUserName();
1748                String cdlFill = StringUtils.repeat("&nbsp;", 5);  // NOI18N
1749                String cdlLine = "<html>" + cdlNames + cdlFill + "<strong>[ " + cdlStatus + " ]</strong></html>";  // NOI18N
1750                return cdlLine;
1751
1752            case "Antecedent":  // NOI18N
1753                cdl = (Conditional) component;
1754                String antecedent = translateAntecedent(cdl.getAntecedentExpression(), false);
1755                if (cdl.getLogicType() != Conditional.AntecedentOperator.MIXED) {
1756                    antecedent = "- - - - - - - - -";
1757                }
1758                return Bundle.getMessage("LogixAntecedent") + " " + antecedent;   // NOI18N
1759
1760            case "LogicType":  // NOI18N
1761                cdl = (Conditional) component;
1762                Conditional.AntecedentOperator logicType = cdl.getLogicType();
1763                String logicName; // used for display only
1764                switch (logicType) {
1765                    case ALL_AND:
1766                        logicName = Bundle.getMessage("LogicAND");      // NOI18N
1767                        break;
1768                    case ALL_OR:
1769                        logicName = Bundle.getMessage("LogicOR");       // NOI18N
1770                        break;
1771                    case MIXED:
1772                        logicName = Bundle.getMessage("LogicMixed");    // NOI18N
1773                        break;
1774                    default:
1775                        logicName = "None"; // only used for invalid LogicType
1776                }
1777                return Bundle.getMessage("LabelLogicTypeActions") + "  " + logicName;   // NOI18N
1778
1779            case "TriggerMode":  // NOI18N
1780                cdl = (Conditional) component;
1781                boolean triggerMode = cdl.getTriggerOnChange();
1782                String triggerText;
1783                if (triggerMode) {
1784                    triggerText = Bundle.getMessage("triggerOnChange"); // NOI18N
1785                } else {
1786                    triggerText = Bundle.getMessage("triggerOnAny");    // NOI18N
1787                }
1788                return Bundle.getMessage("LabelTriggerModeActions") + "  " + triggerText; // NOI18N
1789
1790            case "Variables":  // NOI18N
1791                if (idx == 0) {
1792                    // The node is not expanded, return plain content
1793                    return Bundle.getMessage("NodeVariablesCollapsed");  // NOI18N
1794                } else {
1795                    // The node is expanded, include the field names
1796                    return String.format("%s   [[ %s || %s || %s ]]", // NOI18N
1797                            Bundle.getMessage("NodeVariablesExpanded"), // NOI18N
1798                            Bundle.getMessage("ColumnLabelDescription"), // NOI18N
1799                            Bundle.getMessage("ColumnLabelTriggersCalculation"), // NOI18N
1800                            Bundle.getMessage("ColumnState")); // NOI18N
1801                }
1802
1803            case "Variable":  // NOI18N
1804                var = (ConditionalVariable) component;
1805
1806                String rowNum = "R" + (idx + 1) + (idx > 9 ? " " : "  "); // NOI18N
1807                String rowOper = var.getOpernString() + " ";
1808
1809                String rowNot = "";
1810                if (var.isNegated()) {
1811                    rowNot = Bundle.getMessage("LogicNOT") + " ";     // NOI18N
1812                }
1813
1814                String boldFormat = "  || <strong>%s</strong>";  // NOI18N
1815                String rowTrigger = String.format(boldFormat,
1816                        (var.doTriggerActions()) ? Bundle.getMessage("ButtonYes") : Bundle.getMessage("ButtonNo"));  // NOI18N
1817                String rowStatus = String.format(boldFormat,
1818                        (var.evaluate()) ? Bundle.getMessage("True") : Bundle.getMessage("False"));  // NOI18N
1819
1820                String varLine = "<html>" + rowNum + rowOper + rowNot + var.toString() + rowTrigger + rowStatus + "</html>";  // NOI18N
1821                return varLine;
1822
1823            case "Actions":  // NOI18N
1824                return Bundle.getMessage("NodeActions");  // NOI18N
1825
1826            case "Action":   // NOI18N
1827                act = (ConditionalAction) component;
1828                return act.description(_triggerMode);
1829
1830            default:
1831                return "None";
1832        }
1833    }
1834
1835    /**
1836     * Display reminder to finish a node before starting another.  This is a session reminder.
1837     */
1838    void showNodeEditMessage() {
1839        if (InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class) != null) {
1840            InstanceManager.getDefault(jmri.UserPreferencesManager.class).
1841                    showInfoMessage(Bundle.getMessage("NodeEditTitle"), // NOI18N
1842                            Bundle.getMessage("NodeEditText"), // NOI18N
1843                            getClassName(),
1844                            "SkipNodeEditMessage", // NOI18N
1845                            true,
1846                            false);
1847        }
1848    }
1849
1850    /**
1851     * Focus gained implies intent to make changes, set up for edit.
1852     */
1853    transient FocusListener detailFocusEvent = new FocusListener() {
1854        @Override
1855        public void focusGained(FocusEvent e) {
1856            if (!_editActive) {
1857                setEditMode(true);
1858            }
1859        }
1860
1861        @Override
1862        public void focusLost(FocusEvent e) {
1863        }
1864    };
1865
1866    /**
1867     * Add the focus listener to each detail edit field.
1868     */
1869    void setFocusListeners() {
1870        _editConditionalUserName.addFocusListener(detailFocusEvent);
1871        _editAntecedent.addFocusListener(detailFocusEvent);
1872        _editOperatorMode.addFocusListener(detailFocusEvent);
1873        _variableItemBox.addFocusListener(detailFocusEvent);
1874        _variableOperBox.addFocusListener(detailFocusEvent);
1875        _variableNegated.addFocusListener(detailFocusEvent);
1876        _variableTriggerActions.addFocusListener(detailFocusEvent);
1877        _variableNameField.addFocusListener(detailFocusEvent);
1878        _variableStateBox.addFocusListener(detailFocusEvent);
1879        _variableSignalBox.addFocusListener(detailFocusEvent);
1880        _selectLogixBox.addFocusListener(detailFocusEvent);
1881        _selectConditionalBox.addFocusListener(detailFocusEvent);
1882        _variableCompareOpBox.addFocusListener(detailFocusEvent);
1883        _variableCompareTypeBox.addFocusListener(detailFocusEvent);
1884        _variableData1Field.addFocusListener(detailFocusEvent);
1885        _variableData2Field.addFocusListener(detailFocusEvent);
1886        _actionItemBox.addFocusListener(detailFocusEvent);
1887        _actionNameField.addFocusListener(detailFocusEvent);
1888        _actionTypeBox.addFocusListener(detailFocusEvent);
1889        _actionBox.addFocusListener(detailFocusEvent);
1890        _shortActionString.addFocusListener(detailFocusEvent);
1891        _longActionString.addFocusListener(detailFocusEvent);
1892        _actionSetButton.addFocusListener(detailFocusEvent);
1893        _actionOptionBox.addFocusListener(detailFocusEvent);
1894    }
1895
1896    /**
1897     * Enable/disable buttons based on edit state.
1898     * Open pick lists based on the current SelectionMode.
1899     * The edit state controls the ability to select tree nodes.
1900     *
1901     * @param active True to make edit active, false to make edit inactive
1902     */
1903    void setEditMode(boolean active) {
1904        _editActive = active;
1905        _cancelAction.setEnabled(active);
1906        _updateAction.setEnabled(active);
1907        Component delButton = _deleteButtonPanel.getComponent(0);
1908        if (delButton instanceof JButton) {
1909            delButton.setEnabled(!active);
1910        }
1911        Component addButton = _addButtonPanel.getComponent(0);
1912        if (addButton instanceof JButton) {
1913            addButton.setEnabled(!active);
1914        }
1915        if (_curNodeType != null) {
1916            if (_curNodeType.equals("Conditional") || _curNodeType.equals("Variable") || _curNodeType.equals("Action")) {  // NOI18N
1917                setMoveButtons();
1918            }
1919        }
1920        if (active) {
1921            setPickWindow("Activate", Conditional.ItemType.NONE);  // NOI18N
1922        } else {
1923            setPickWindow("Deactivate", Conditional.ItemType.NONE);  // NOI18N
1924        }
1925    }
1926
1927    /**
1928     * Ceate tabbed Pick Taba;e or Pick Single based on Selection Mode.
1929     * Called by {@link #setEditMode} when edit mode becomes active.
1930     * Called by {@link #variableTypeChanged} and {@link #actionItemChanged} when item type changes.
1931     *
1932     * @param cmd The source or action to be performed.
1933     * @param item The item type for Variable or Action or zero
1934     */
1935    void setPickWindow(String cmd, Conditional.ItemType item) {
1936        if (_selectionMode == SelectionMode.USECOMBO) {
1937            return;
1938        }
1939        // Save the item information
1940        if (cmd.equals("Variable") || cmd.equals("Action")) {  // NOI18N
1941            _pickCommand = cmd;
1942            _pickItem = item;
1943            if (_editActive) {
1944                if (_selectionMode == SelectionMode.USEMULTI) {
1945                    doPickList();
1946                } else {
1947                    doPickSingle();
1948                }
1949            }
1950        }
1951        if (cmd.equals("Activate")) {  // NOI18N
1952            if (_curNodeType.equals("Variable") || _curNodeType.equals("Action")) {  // NOI18N
1953                // Open the appropriate window based on the save values
1954                if (_selectionMode == SelectionMode.USEMULTI) {
1955                    doPickList();
1956                } else {
1957                    doPickSingle();
1958                }
1959            }
1960        }
1961        if (cmd.equals("Deactivate")) {  // NOI18N
1962            // Close/dispose the pick window
1963            hidePickListTable();
1964            closeSinglePanelPickList();
1965        }
1966    }
1967
1968    /**
1969     * Create a Variable or Action based tabbed PickList with appropriate tab selected.
1970     */
1971    void doPickList() {
1972        if (_pickItem == Conditional.ItemType.NONE) {
1973            return;
1974        }
1975        if (_pickTables == null) {
1976            openPickListTable();
1977        }
1978        if (_pickCommand.equals("Variable")) {
1979            setPickListTab(_pickItem, false);
1980        } else if (_pickCommand.equals("Action")) {
1981            setPickListTab(_pickItem, true);
1982        }
1983    }
1984
1985    /**
1986     * Create a Variable or Action based single pane PickList.
1987     */
1988    void doPickSingle() {
1989        if (_pickCommand.equals("Variable")) {
1990            createSinglePanelPickList(_pickItem, new PickSingleListener(_variableNameField, _pickItem), false);
1991        } else if (_pickCommand.equals("Action")) {
1992            createSinglePanelPickList(_pickItem, new PickSingleListener(_actionNameField, _pickItem), true);
1993        }
1994    }
1995
1996    // ============  Edit Variable Section ============
1997
1998    /**
1999     * Called once during class initialization to define the GUI objects. Where
2000     * possible, the combo box content is loaded.
2001     */
2002    void buildVariableComponents() {
2003        // Item Type
2004        _variableItemBox = new JComboBox<>();
2005        for (Conditional.ItemType itemType : Conditional.ItemType.getStateVarList()) {
2006            _variableItemBox.addItem(itemType);
2007        }
2008        JComboBoxUtil.setupComboBoxMaxRows(_variableItemBox);
2009        _variableItemBox.addActionListener(new ActionListener() {
2010            @Override
2011            public void actionPerformed(ActionEvent e) {
2012                Conditional.ItemType newVariableItem = _variableItemBox.getItemAt(_variableItemBox.getSelectedIndex());
2013                if (log.isDebugEnabled()) {
2014                    log.debug("_variableItemBox Listener: new = {}, curr = {}, row = {}",  // NOI18N
2015                            newVariableItem, _curVariableItem, _curNodeRow);
2016                }
2017                if (newVariableItem != _curVariableItem) {
2018                    if (_curNodeRow >= 0) {
2019                        _curVariable = new ConditionalVariable();
2020                        _variableList.set(_curNodeRow, _curVariable);
2021                    }
2022                    _curVariableItem = newVariableItem;
2023                }
2024                variableTypeChanged(newVariableItem);
2025            }
2026        });
2027
2028        // Oper type
2029        _variableOperBox = new JComboBox<String>();
2030        _variableOperBox.addItem(Bundle.getMessage("LogicAND"));  // NOI18N
2031        _variableOperBox.addItem(Bundle.getMessage("LogicOR"));  // NOI18N
2032
2033        // Negation
2034        _variableNegated = new JCheckBox();
2035
2036        // trigger
2037        _variableTriggerActions = new JCheckBox();
2038
2039        // Item Name
2040        _variableNameField = new JTextField(20);
2041
2042        // Combo box section for selecting conditional reference
2043        //   First box selects the Logix, the second selects the conditional within the logix
2044        _selectLogixBox.addItem("XXXXXXXXXXXXXXXXXXXXX");  // NOI18N
2045        _selectConditionalBox.addItem("XXXXXXXXXXXXXXXXXXXXX");  // NOI18N
2046        _selectLogixBox.addActionListener(selectLogixBoxListener);
2047        _selectConditionalBox.addActionListener(selectConditionalBoxListener);
2048
2049        // State Box
2050        _variableStateBox = new JComboBox<>();
2051        _variableStateBox.addItem(Conditional.Type.XXXXXXX);  // NOI18N
2052
2053        // Aspects
2054        _variableSignalBox = new JComboBox<>();
2055        _variableSignalBox.addItem("XXXXXXXXX");  // NOI18N
2056
2057        // Compare operator
2058        _variableCompareOpBox = new JComboBox<>();
2059        for (int i = 1; i <= ConditionalVariable.NUM_COMPARE_OPERATIONS; i++) {
2060            _variableCompareOpBox.addItem(ConditionalVariable.getCompareOperationString(i));
2061        }
2062
2063        // Compare type
2064        _variableCompareTypeBox = new JComboBox<>();
2065        for (Conditional.Type type : Conditional.Type.getMemoryItems()) {
2066            _variableCompareTypeBox.addItem(type);
2067        }
2068        _variableCompareTypeBox.addActionListener(compareTypeBoxListener);
2069
2070        // Data 1
2071        _variableData1Field = new JTextField(10);
2072
2073        // Data 2
2074        _variableData2Field = new JTextField(10);
2075    }
2076
2077    // ------------ Make Variable Edit Grid Panels ------------
2078
2079    /**
2080     * Create a one row grid with just the Variable Type box. This is the base
2081     * for larger grids as well as the initial grid for new State Variables.
2082     *
2083     * @param c the constraints object used for the grid construction
2084     */
2085    void makeEmptyVariableGrid(GridBagConstraints c) {
2086        // Variable type box
2087        c.gridy = 0;
2088        c.gridx = 0;
2089        c.anchor = java.awt.GridBagConstraints.EAST;
2090        JLabel row0Label = new JLabel(Bundle.getMessage("LabelVariableType"));  // NOI18N
2091        row0Label.setToolTipText(Bundle.getMessage("VariableTypeHint"));  // NOI18N
2092        _gridPanel.add(row0Label, c);
2093        c.gridx = 1;
2094        c.anchor = java.awt.GridBagConstraints.WEST;
2095        _gridPanel.add(_variableItemBox, c);
2096    }
2097
2098    /*
2099     * Create the Oper, Not and Trigger rows.
2100     *
2101     * @param c The constraints object used for the grid construction
2102     */
2103    void makeOptionsVariableGrid(GridBagConstraints c) {
2104        makeEmptyVariableGrid(c);
2105
2106        // Oper Select
2107        c.gridy = 1;
2108        c.gridx = 0;
2109        c.anchor = java.awt.GridBagConstraints.EAST;
2110        JLabel row1Label = new JLabel(Bundle.getMessage("ColumnLabelOperator"));  // NOI18N
2111        row1Label.setToolTipText(Bundle.getMessage("VariableOperHint"));  // NOI18N
2112        _gridPanel.add(row1Label, c);
2113        c.gridx = 1;
2114        c.anchor = java.awt.GridBagConstraints.WEST;
2115        _gridPanel.add(_variableOperBox, c);
2116
2117        // Not Select
2118        c.gridy = 2;
2119        c.gridx = 0;
2120        c.anchor = java.awt.GridBagConstraints.EAST;
2121        JLabel row2Label = new JLabel(Bundle.getMessage("ColumnLabelNot"));  // NOI18N
2122        row2Label.setToolTipText(Bundle.getMessage("VariableNotHint"));  // NOI18N
2123        _gridPanel.add(row2Label, c);
2124        c.gridx = 1;
2125        c.anchor = java.awt.GridBagConstraints.WEST;
2126        _gridPanel.add(_variableNegated, c);
2127
2128        // Trigger Select
2129        c.gridy = 3;
2130        c.gridx = 0;
2131        c.anchor = java.awt.GridBagConstraints.EAST;
2132        JLabel row3Label = new JLabel(Bundle.getMessage("ColumnLabelTriggersCalculation"));  // NOI18N
2133        row3Label.setToolTipText(Bundle.getMessage("VariableTriggerHint"));  // NOI18N
2134        _gridPanel.add(row3Label, c);
2135        c.gridx = 1;
2136        c.anchor = java.awt.GridBagConstraints.WEST;
2137        _gridPanel.add(_variableTriggerActions, c);
2138    }
2139
2140    /**
2141     * Create the standard Name and State rows.
2142     * The name field will be either a text field or a combo box.
2143     * The name field label is a variable to support run time changes.
2144     *
2145     * @param c The constraints object used for the grid construction
2146     */
2147    void makeStandardVariableGrid(GridBagConstraints c) {
2148        makeOptionsVariableGrid(c);
2149
2150        // Name Field
2151        c.gridy = 4;
2152        c.gridx = 0;
2153        c.anchor = java.awt.GridBagConstraints.EAST;
2154        _gridPanel.add(_variableNameLabel, c);
2155        c.gridx = 1;
2156        c.anchor = java.awt.GridBagConstraints.WEST;
2157        if (_selectionMode == SelectionMode.USECOMBO) {
2158            _gridPanel.add(_comboNameBox, c);
2159        } else {
2160            _gridPanel.add(_variableNameField, c);
2161        }
2162
2163        // State Box
2164        c.gridy = 5;
2165        c.gridx = 0;
2166        c.anchor = java.awt.GridBagConstraints.EAST;
2167        JLabel row5Label = new JLabel(Bundle.getMessage("LabelVariableState"));  // NOI18N
2168        row5Label.setToolTipText(Bundle.getMessage("VariableStateHint"));  // NOI18N
2169        _gridPanel.add(row5Label, c);
2170        c.gridx = 1;
2171        c.anchor = java.awt.GridBagConstraints.WEST;
2172        _gridPanel.add(_variableStateBox, c);
2173    }
2174
2175    /**
2176     * Add the Aspect field for signal heads and signal masts.
2177     *
2178     * @param c The constraints object used for the grid construction
2179     */
2180    void makeSignalAspectVariableGrid(GridBagConstraints c) {
2181        makeStandardVariableGrid(c);
2182
2183        // Mast Aspect Box
2184        c.gridy = 6;
2185        c.gridx = 0;
2186        c.anchor = java.awt.GridBagConstraints.EAST;
2187        JLabel row5Label = new JLabel(Bundle.getMessage("LabelVariableAspect"));  // NOI18N
2188        row5Label.setToolTipText(Bundle.getMessage("VariableAspectHint"));  // NOI18N
2189        _gridPanel.add(row5Label, c);
2190        c.gridx = 1;
2191        c.anchor = java.awt.GridBagConstraints.WEST;
2192        _gridPanel.add(_variableSignalBox, c);
2193    }
2194
2195    /**
2196     * Create the Logix and Conditional rows and the State row.
2197     *
2198     * @param c The constraints object used for the grid construction
2199     */
2200    void makeConditionalVariableGrid(GridBagConstraints c) {
2201        makeOptionsVariableGrid(c);
2202
2203        // Logix Selection ComboBox
2204        c.gridy = 4;
2205        c.gridx = 0;
2206        c.anchor = java.awt.GridBagConstraints.EAST;
2207        JLabel row4Label = new JLabel(Bundle.getMessage("SelectLogix"));  // NOI18N
2208        row4Label.setToolTipText(Bundle.getMessage("VariableLogixHint"));  // NOI18N
2209        _gridPanel.add(row4Label, c);
2210        c.gridx = 1;
2211        c.anchor = java.awt.GridBagConstraints.WEST;
2212        _gridPanel.add(_selectLogixBox, c);
2213
2214        // Conditional Selection ComboBox
2215        c.gridy = 5;
2216        c.gridx = 0;
2217        c.anchor = java.awt.GridBagConstraints.EAST;
2218        JLabel row5Label = new JLabel(Bundle.getMessage("SelectConditional"));  // NOI18N
2219        row5Label.setToolTipText(Bundle.getMessage("VariableConditionalHint"));  // NOI18N
2220        _gridPanel.add(row5Label, c);
2221        c.gridx = 1;
2222        c.anchor = java.awt.GridBagConstraints.WEST;
2223        _gridPanel.add(_selectConditionalBox, c);
2224
2225        // State Box
2226        c.gridy = 6;
2227        c.gridx = 0;
2228        c.anchor = java.awt.GridBagConstraints.EAST;
2229        JLabel row6Label = new JLabel(Bundle.getMessage("LabelVariableState"));  // NOI18N
2230        row6Label.setToolTipText(Bundle.getMessage("VariableStateHint"));  // NOI18N
2231        _gridPanel.add(row6Label, c);
2232        c.gridx = 1;
2233        c.anchor = java.awt.GridBagConstraints.WEST;
2234        _gridPanel.add(_variableStateBox, c);
2235    }
2236
2237    /**
2238     * Create the Memory specific rows.
2239     *
2240     * @param c The constraints object used for the grid construction
2241     */
2242    void makeMemoryVariableGrid(GridBagConstraints c) {
2243        makeOptionsVariableGrid(c);
2244
2245        // Name Field
2246        c.gridy = 4;
2247        c.gridx = 0;
2248        c.anchor = java.awt.GridBagConstraints.EAST;
2249        JLabel row4Label = new JLabel(Bundle.getMessage("LabelItemName"));  // NOI18N
2250        row4Label.setToolTipText(Bundle.getMessage("NameHintMemory"));  // NOI18N
2251        _gridPanel.add(row4Label, c);
2252        c.gridx = 1;
2253        c.anchor = java.awt.GridBagConstraints.WEST;
2254        if (_selectionMode == SelectionMode.USECOMBO) {
2255            _gridPanel.add(_comboNameBox, c);
2256        } else {
2257            _gridPanel.add(_variableNameField, c);
2258        }
2259
2260        // Comparison Operator
2261        c.gridy = 5;
2262        c.gridx = 0;
2263        c.anchor = java.awt.GridBagConstraints.EAST;
2264        JLabel row5Label = new JLabel(Bundle.getMessage("LabelCompareOp"));  // NOI18N
2265        row5Label.setToolTipText(Bundle.getMessage("CompareHintMemory"));  // NOI18N
2266        _gridPanel.add(row5Label, c);
2267        c.gridx = 1;
2268        c.anchor = java.awt.GridBagConstraints.WEST;
2269        _gridPanel.add(_variableCompareOpBox, c);
2270
2271        // Compare As
2272        c.gridy = 6;
2273        c.gridx = 0;
2274        c.anchor = java.awt.GridBagConstraints.EAST;
2275        JLabel row6Label = new JLabel(Bundle.getMessage("LabelCompareType"));  // NOI18N
2276        row6Label.setToolTipText(Bundle.getMessage("CompareTypeHint"));  // NOI18N
2277        _gridPanel.add(row6Label, c);
2278        c.gridx = 1;
2279        c.anchor = java.awt.GridBagConstraints.WEST;
2280        _gridPanel.add(_variableCompareTypeBox, c);
2281
2282        // Literal Value (default) / Memory Value (name)
2283        c.gridy = 7;
2284        c.gridx = 0;
2285        c.anchor = java.awt.GridBagConstraints.EAST;
2286        _gridPanel.add(_variableMemoryValueLabel, c);
2287        c.gridx = 1;
2288        c.anchor = java.awt.GridBagConstraints.WEST;
2289        _gridPanel.add(_variableData1Field, c);
2290    }
2291
2292    /**
2293     * Create the Fast Clock start and end time rows.
2294     *
2295     * @param c The constraints object used for the grid construction
2296     */
2297    void makeFastClockVariableGrid(GridBagConstraints c) {
2298        makeOptionsVariableGrid(c);
2299
2300        // Start Time Field
2301        c.gridy = 4;
2302        c.gridx = 0;
2303        c.anchor = java.awt.GridBagConstraints.EAST;
2304        JLabel row4Label = new JLabel(Bundle.getMessage("LabelStartTime"));  // NOI18N
2305        row4Label.setToolTipText(Bundle.getMessage("DataHintTime"));  // NOI18N
2306        _gridPanel.add(row4Label, c);
2307        c.gridx = 1;
2308        c.anchor = java.awt.GridBagConstraints.WEST;
2309        _gridPanel.add(_variableData1Field, c);
2310
2311        // End Time Field
2312        c.gridy = 5;
2313        c.gridx = 0;
2314        c.anchor = java.awt.GridBagConstraints.EAST;
2315        JLabel row5Label = new JLabel(Bundle.getMessage("LabelEndTime"));  // NOI18N
2316        row5Label.setToolTipText(Bundle.getMessage("DataHintTime"));  // NOI18N
2317        _gridPanel.add(row5Label, c);
2318        c.gridx = 1;
2319        c.anchor = java.awt.GridBagConstraints.WEST;
2320        _gridPanel.add(_variableData2Field, c);
2321    }
2322
2323    // ------------ Main Variable methods ------------
2324
2325    /**
2326     * Set display to show current state variable (curVariable) parameters.
2327     */
2328    void initializeStateVariables() {
2329        Conditional.Type testType = _curVariable.getType();
2330        if (log.isDebugEnabled()) {
2331            log.debug("initializeStateVariables: testType= {}", testType);  // NOI18N
2332        }
2333        Conditional.ItemType itemType = testType.getItemType();
2334        log.debug("initializeStateVariables: itemType= {}, testType= {}", itemType, testType);  // NOI18N
2335        if (itemType == _variableItemBox.getSelectedItem()) {
2336            // Force a refresh of variableTypeChanged
2337            variableTypeChanged(itemType);
2338        }
2339        _variableItemBox.setSelectedItem(itemType);
2340        _variableOperBox.setSelectedItem(_curVariable.getOpernString());
2341        _variableNegated.setSelected(_curVariable.isNegated());
2342        _variableTriggerActions.setSelected(_curVariable.doTriggerActions());
2343
2344        switch (itemType) {
2345            case NONE:
2346                _variableNameField.setText("");
2347                break;
2348
2349            case SENSOR:
2350            case TURNOUT:
2351            case LIGHT:
2352            case CONDITIONAL:
2353            case WARRANT:
2354                _variableStateBox.setSelectedItem(testType);
2355                _variableNameField.setText(_curVariable.getName());
2356                break;
2357
2358            case SIGNALHEAD:
2359                _variableStateBox.setSelectedItem(testType);
2360                _variableNameField.setText(_curVariable.getName());
2361                if (Conditional.Type.isSignalHeadApperance(testType)) {
2362                    _variableStateBox.setSelectedItem(Conditional.Type.SIGNAL_HEAD_APPEARANCE_EQUALS);
2363                    _variableSignalBox.setSelectedItem(_curVariable.getType());
2364                }
2365                break;
2366
2367            case SIGNALMAST:
2368                // set display to show current state variable (curVariable) parameters
2369                _variableStateBox.setSelectedItem(testType);
2370                _variableNameField.setText(_curVariable.getName());
2371                if (testType == Conditional.Type.SIGNAL_MAST_ASPECT_EQUALS) {
2372                    _variableSignalBox.setSelectedItem(_curVariable.getDataString());
2373                }
2374                break;
2375
2376            case MEMORY:
2377                _variableCompareTypeBox.setSelectedIndex(
2378                        Conditional.Type.getIndexInList(Conditional.Type.getMemoryItems(), testType));
2379                _variableNameField.setText(_curVariable.getName());
2380                int num1 = _curVariable.getNum1() - 1;
2381                if (num1 == -1) {  // former code was only equals
2382                    num1 = ConditionalVariable.EQUAL - 1;
2383                }
2384                _variableCompareOpBox.setSelectedIndex(num1);
2385                _variableData1Field.setText(_curVariable.getDataString());
2386                break;
2387
2388            case CLOCK:
2389                int time = _curVariable.getNum1();
2390                _variableData1Field.setText(formatTime(time / 60, time - ((time / 60) * 60)));
2391                time = _curVariable.getNum2();
2392                _variableData2Field.setText(formatTime(time / 60, time - ((time / 60) * 60)));
2393                _variableNameField.setText("");
2394                break;
2395
2396            case OBLOCK:
2397                _variableNameField.setText(_curVariable.getName());
2398                //_variableStateBox.removeAllItems();
2399                for (Conditional.Type type : Conditional.Type.getOBlockItems()) {
2400                    _variableStateBox.addItem(type);
2401                    if (type.toString().equals(OBlock.getLocalStatusName(_curVariable.getDataString()))) {
2402                        _variableStateBox.setSelectedItem(type);
2403                    }
2404                }
2405//                Iterator<String> names = OBlock.getLocalStatusNames();
2406//                while (names.hasNext()) {
2407//                    _variableStateBox.addItem(names.next());
2408//                }
2409//                _variableStateBox.setSelectedItem(OBlock.getLocalStatusName(_curVariable.getDataString()));
2410                break;
2411
2412            case ENTRYEXIT:
2413                _variableNameField.setText(_curVariable.getBean().getUserName());
2414                _variableStateBox.setSelectedItem(testType);
2415                break;
2416
2417            default:
2418                break;
2419        }
2420        _detailGrid.setVisible(true);
2421    }
2422
2423    /**
2424     * Respond to change in variable type chosen in the State Variable combo box.
2425     *
2426     * @param itemType value representing the newly selected Conditional type,
2427     *                 i.e. ITEM_TYPE_SENSOR
2428     */
2429    private void variableTypeChanged(Conditional.ItemType itemType) {
2430        Conditional.Type testType = _curVariable.getType();
2431        log.debug("variableTypeChanged: itemType= {}, testType= {}", itemType, testType);  // NOI18N
2432        _variableStateBox.removeAllItems();
2433        _variableNameField.removeActionListener(variableSignalHeadNameListener);
2434        _variableNameField.removeActionListener(variableSignalMastNameListener);
2435        _variableStateBox.removeActionListener(variableSignalTestStateListener);
2436        _detailGrid.setVisible(false);
2437
2438        if (_comboNameBox != null) {
2439            for (ActionListener item : _comboNameBox.getActionListeners()) {
2440                _comboNameBox.removeActionListener(item);
2441            }
2442            _comboNameBox.removeFocusListener(detailFocusEvent);
2443        }
2444        setPickWindow("Variable", itemType);  // NOI18N
2445
2446        _variableOperBox.setSelectedItem(_curVariable.getOpernString());
2447        _variableNegated.setSelected(_curVariable.isNegated());
2448        _variableTriggerActions.setSelected(_curVariable.doTriggerActions());
2449
2450        switch (itemType) {
2451            case NONE:
2452                makeDetailGrid("EmptyVariable");  // NOI18N
2453                break;
2454
2455            case SENSOR:
2456                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintSensor"));  // NOI18N
2457                for (Conditional.Type type : Conditional.Type.getSensorItems()) {
2458                    _variableStateBox.addItem(type);
2459                }
2460                setVariableNameBox(itemType);
2461                makeDetailGrid("StandardVariable");  // NOI18N
2462                break;
2463
2464            case TURNOUT:
2465                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintTurnout"));  // NOI18N
2466                for (Conditional.Type type : Conditional.Type.getTurnoutItems()) {
2467                    _variableStateBox.addItem(type);
2468                }
2469                setVariableNameBox(itemType);
2470                makeDetailGrid("StandardVariable");  // NOI18N
2471                break;
2472
2473            case LIGHT:
2474                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintLight"));  // NOI18N
2475                for (Conditional.Type type : Conditional.Type.getLightItems()) {
2476                    _variableStateBox.addItem(type);
2477                }
2478                setVariableNameBox(itemType);
2479                makeDetailGrid("StandardVariable");  // NOI18N
2480                break;
2481
2482            case SIGNALHEAD:
2483                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintSignal"));  // NOI18N
2484                loadJComboBoxWithHeadAppearances(_variableSignalBox, _variableNameField.getText().trim());
2485
2486                for (Conditional.Type type : Conditional.Type.getSignalHeadStateMachineItems()) {
2487                    _variableStateBox.addItem(type);
2488                }
2489
2490                setVariableNameBox(itemType);
2491                if (testType == Conditional.Type.SIGNAL_HEAD_APPEARANCE_EQUALS) {
2492                    makeDetailGrid("SignalAspectVariable");  // NOI18N
2493                } else {
2494                    makeDetailGrid("StandardVariable");  // NOI18N
2495                }
2496
2497                _variableNameField.addActionListener(variableSignalHeadNameListener);
2498                _variableStateBox.addActionListener(variableSignalTestStateListener);
2499                break;
2500
2501            case SIGNALMAST:
2502                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintSignalMast"));  // NOI18N
2503                _variableNameField.addActionListener(variableSignalMastNameListener);
2504                _variableStateBox.addActionListener(variableSignalTestStateListener);
2505                loadJComboBoxWithMastAspects(_variableSignalBox, _variableNameField.getText().trim());
2506
2507                for (Conditional.Type type : Conditional.Type.getSignalMastItems()) {
2508                    _variableStateBox.addItem(type);
2509                }
2510                setVariableNameBox(itemType);
2511                if (testType == Conditional.Type.SIGNAL_MAST_ASPECT_EQUALS) {
2512                    makeDetailGrid("SignalAspectVariable");  // NOI18N
2513                } else {
2514                    makeDetailGrid("StandardVariable");  // NOI18N
2515                }
2516                break;
2517
2518            case MEMORY:
2519                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintMemory"));  // NOI18N
2520                setVariableNameBox(itemType);
2521                makeDetailGrid("MemoryVariable");  // NOI18N
2522                compareTypeChanged(testType);   // Force the label update
2523                break;
2524
2525            case CONDITIONAL:
2526                for (Conditional.Type type : Conditional.Type.getConditionalItems()) {
2527                    _variableStateBox.addItem(type);
2528                }
2529                loadSelectLogixBox();
2530                makeDetailGrid("ConditionalVariable");  // NOI18N
2531                _selectLogixBox.addActionListener(selectLogixBoxListener);
2532                _selectConditionalBox.addActionListener(selectConditionalBoxListener);
2533                break;
2534
2535            case WARRANT:
2536                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintWarrant"));  // NOI18N
2537                for (Conditional.Type type : Conditional.Type.getWarrantItems()) {
2538                    _variableStateBox.addItem(type);
2539                }
2540                setVariableNameBox(itemType);
2541                makeDetailGrid("StandardVariable");  // NOI18N
2542                break;
2543
2544            case CLOCK:
2545                makeDetailGrid("FastClockVariable");  // NOI18N
2546                break;
2547
2548            case OBLOCK:
2549                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintOBlock"));  // NOI18N
2550                _variableStateBox.removeAllItems();
2551                for (Conditional.Type type : Conditional.Type.getOBlockItems()) {
2552                    _variableStateBox.addItem(type);
2553                }
2554//                Iterator<String> names = OBlock.getLocalStatusNames();
2555//                while (names.hasNext()) {
2556//                    _variableStateBox.addItem(names.next());
2557//                }
2558                setVariableNameBox(itemType);
2559                makeDetailGrid("StandardVariable");  // NOI18N
2560                break;
2561
2562            case ENTRYEXIT:
2563                _variableNameLabel.setToolTipText(Bundle.getMessage("NameHintEntryExit"));  // NOI18N
2564                _variableNameField.setText(_curVariable.getName());
2565                for (Conditional.Type type : Conditional.Type.getEntryExitItems()) {
2566                    _variableStateBox.addItem(type);
2567                }
2568                setVariableNameBox(itemType);
2569                makeDetailGrid("StandardVariable");  // NOI18N
2570                break;
2571
2572            default:
2573                break;
2574        }
2575    }
2576
2577    /**
2578     * Update the name combo box selection based on the current contents of the
2579     * name field. Called by variableItemChanged.
2580     *
2581     * @since 4.7.3
2582     * @param itemType The item type, such as sensor or turnout.
2583     */
2584    void setVariableNameBox(Conditional.ItemType itemType) {
2585        if (_selectionMode != SelectionMode.USECOMBO) {
2586            return;
2587        }
2588        _comboNameBox = createNameBox(itemType);
2589        if (_comboNameBox == null) {
2590            return;
2591        }
2592        // Select the current entry, add the listener
2593        _comboNameBox.setSelectedItemByName(_curVariable.getName());
2594        _comboNameBox.addActionListener(new NameBoxListener(_variableNameField));
2595        _comboNameBox.addFocusListener(detailFocusEvent);
2596    }
2597
2598    // ------------ Variable detail methods ------------
2599
2600    /**
2601     * Respond to Cancel variable button
2602     */
2603    void cancelEditVariable() {
2604        if (_newVariableItem) {
2605            _newVariableItem = false;
2606            deletePressed();
2607        }
2608        cleanUpVariable();
2609    }
2610
2611    /**
2612     * Respond to Update Variable button in the Edit Variable pane.
2613     */
2614    void updateVariable() {
2615        if (!validateVariable()) {
2616            return;
2617        }
2618        _newVariableItem = false;
2619        _curConditional.setStateVariables(_variableList);
2620
2621        // Update conditional references
2622        TreeSet<String> newTargetNames = new TreeSet<>();
2623        loadReferenceNames(_variableList, newTargetNames);
2624        updateWhereUsed(_oldTargetNames, newTargetNames, _curNodeName);
2625
2626        // Update the tree nodeChanged
2627        _curNode.setText(buildNodeText("Variable", _curVariable, _curNodeRow));  // NOI18N
2628        _cdlModel.nodeChanged(_curNode);
2629        cleanUpVariable();
2630    }
2631
2632    /**
2633     * Clean up: Cancel, Update and Delete Variable buttons.
2634     */
2635    void cleanUpVariable() {
2636        if (_logicType != Conditional.AntecedentOperator.MIXED) {
2637            setMoveButtons();
2638        }
2639    }
2640
2641    /**
2642     * Load the Logix selection box. Set the selection to the current Logix.
2643     *
2644     * @since 4.7.4
2645     */
2646    void loadSelectLogixBox() {
2647        // Get the current Logix name for selecting the current combo box row
2648        String cdlName = _curVariable.getName();
2649        String lgxName;
2650        if (cdlName.length() == 0 || (_curVariable.getType() != Conditional.Type.CONDITIONAL_TRUE
2651                && _curVariable.getType() != Conditional.Type.CONDITIONAL_FALSE)) {
2652            // Use the current logix name for "add" state variable
2653            lgxName = _curLogix.getSystemName();
2654        } else {
2655            Logix x = _conditionalManager.getParentLogix(cdlName);
2656            if (x == null) {
2657                log.error("Unable to find the Logix for {}, using the current Logix", cdlName);  // NOI18N
2658                lgxName = _curLogix.getSystemName();
2659            } else {
2660                lgxName = x.getSystemName();
2661            }
2662        }
2663
2664        _selectLogixBox.removeAllItems();
2665        _selectLogixMap.clear();
2666
2667        String itemKey = "";
2668        for (Logix lgx : _logixManager.getNamedBeanSet()) {
2669            String sName = lgx.getSystemName();
2670            if (sName.equals("SYS")) {  // NOI18N
2671                // Cannot refer to sensor name groups
2672                continue;
2673            }
2674            String uName = lgx.getUserName();
2675            String itemName = "";
2676            if (uName == null || uName.length() < 1) {
2677                itemName = sName;
2678            } else {
2679                itemName = uName + " ( " + sName + " )";
2680            }
2681            _selectLogixMap.put(itemName, sName);
2682            if (lgxName.equals(sName)) {
2683                itemKey = itemName;
2684            }
2685        }
2686
2687        // Load the combo box
2688        for (String item : _selectLogixMap.keySet()) {
2689            _selectLogixBox.addItem(item);
2690        }
2691
2692        JComboBoxUtil.setupComboBoxMaxRows(_selectLogixBox);
2693        _selectLogixBox.setSelectedItem(itemKey);
2694        loadSelectConditionalBox(lgxName);
2695    }
2696
2697    /**
2698     * Load the Conditional selection box. The first row is a prompt.
2699     *
2700     * @since 4.7.4
2701     * @param logixName The Logix system name for selecting the owned
2702     *                  Conditionals
2703     */
2704    void loadSelectConditionalBox(String logixName) {
2705        // Get the current Conditional name for selecting the current combo box row
2706        String cdlName = _curVariable.getName();
2707
2708        _selectConditionalBox.removeAllItems();
2709        _selectConditionalList.clear();
2710
2711        // Create the first row
2712        String itemKey = Bundle.getMessage("SelectFirstRow");  // NOI18N
2713        _selectConditionalBox.addItem(itemKey);
2714        _selectConditionalList.add("-None-");  // NOI18N
2715
2716        Logix x = _logixManager.getBySystemName(logixName);
2717        if (x == null) {
2718            log.error("Logix '{}' not found while building the conditional list", logixName);  // NOI18N
2719            return;
2720        }
2721        if (x.getNumConditionals() == 0) {
2722            return;
2723        }
2724        for (String cName : _conditionalManager.getSystemNameListForLogix(x)) {
2725            Conditional c = _conditionalManager.getConditional(cName);
2726            if (_curConditional.getSystemName().equals(c.getSystemName())) {
2727                // Don't add myself to the list
2728                continue;
2729            }
2730            String uName = c.getUserName();
2731            String itemName = "";
2732            if (uName == null || uName.length() < 1) {
2733                itemName = cName;
2734            } else {
2735                itemName = uName + " ( " + cName + " )";
2736            }
2737            _selectConditionalBox.addItem(itemName);
2738            _selectConditionalList.add(cName);
2739            if (cdlName.equals(cName)) {
2740                itemKey = itemName;
2741            }
2742        }
2743        JComboBoxUtil.setupComboBoxMaxRows(_selectConditionalBox);
2744        _selectConditionalBox.setSelectedItem(itemKey);
2745    }
2746
2747    /**
2748     * Check if Memory type in a Conditional was changed by the user.
2749     * <p>
2750     * Update GUI if it has.
2751     *
2752     * @param testType One of the four types
2753     */
2754    private void compareTypeChanged(Conditional.Type testType) {
2755        if ((testType == Conditional.Type.MEMORY_COMPARE)
2756                || (testType == Conditional.Type.MEMORY_COMPARE_INSENSITIVE)) {
2757            _variableMemoryValueLabel.setText(Bundle.getMessage("LabelMemoryValue"));  // NOI18N
2758            _variableMemoryValueLabel.setToolTipText(Bundle.getMessage("DataHintMemory"));  // NOI18N
2759        } else {
2760            _variableMemoryValueLabel.setText(Bundle.getMessage("LabelLiteralValue"));  // NOI18N
2761            _variableMemoryValueLabel.setToolTipText(Bundle.getMessage("DataHintValue"));  // NOI18N
2762        }
2763    }
2764
2765    /**
2766     * Fetch valid localized appearances for a given Signal Head.
2767     * <p>
2768     * Warn if head is not found.
2769     *
2770     * @param box            the comboBox on the setup pane to fill
2771     * @param signalHeadName user or system name of the Signal Head
2772     */
2773    void loadJComboBoxWithHeadAppearances(JComboBox<String> box, String signalHeadName) {
2774        box.removeAllItems();
2775        log.debug("loadJComboBoxWithSignalHeadAppearances called with name: {}", signalHeadName);  // NOI18N
2776        SignalHead h = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHeadName);
2777        if (h == null) {
2778            box.addItem(Bundle.getMessage("PromptLoadHeadName"));  // NOI18N
2779        } else {
2780            String[] v = h.getValidStateNames();
2781            for (int i = 0; i < v.length; i++) {
2782                box.addItem(v[i]);
2783            }
2784            box.setSelectedItem(h.getAppearanceName());
2785        }
2786    }
2787
2788    /**
2789     * Fetch valid aspects for a given Signal Mast.
2790     * <p>
2791     * Warn if mast is not found.
2792     *
2793     * @param box      the comboBox on the setup pane to fill
2794     * @param mastName user or system name of the Signal Mast
2795     */
2796    void loadJComboBoxWithMastAspects(JComboBox<String> box, String mastName) {
2797        box.removeAllItems();
2798        SignalMast m = InstanceManager.getDefault(jmri.SignalMastManager.class).getSignalMast(mastName);
2799        if (m == null) {
2800            box.addItem(Bundle.getMessage("PromptLoadMastName"));  // NOI18N
2801        } else {
2802            java.util.Vector<String> v = m.getValidAspects();
2803            for (int i = 0; i < v.size(); i++) {
2804                box.addItem(v.get(i));
2805            }
2806            box.setSelectedItem(m.getAspect());
2807        }
2808    }
2809
2810    // ------------ Variable update processes ------------
2811
2812    /**
2813     * Validate Variable data from Edit Variable panel, and transfer it to
2814     * current variable object as appropriate.
2815     * <p>
2816     * Messages are sent to the user for any errors found. This routine returns
2817     * false immediately after finding the first error, even if there might be
2818     * more errors.
2819     *
2820     * @return true if all data checks out OK, otherwise false
2821     */
2822    boolean validateVariable() {
2823        String name = _variableNameField.getText().trim();
2824        _variableNameField.setText(name);
2825        _curVariable.setDataString("");
2826        _curVariable.setNum1(0);
2827        _curVariable.setNum2(0);
2828
2829        updateVariableOperator();
2830        updateVariableNegation();
2831        _curVariable.setTriggerActions(_variableTriggerActions.isSelected());
2832
2833        Conditional.ItemType itemType = _variableItemBox.getItemAt(_variableItemBox.getSelectedIndex());
2834        if (!checkIsAction(name, itemType) ) {
2835            return false;
2836        }
2837        Conditional.Type testType = Conditional.Type.NONE;
2838        switch (itemType) {
2839            case SENSOR:
2840            case TURNOUT:
2841            case LIGHT:
2842            case SIGNALHEAD:
2843            case SIGNALMAST:
2844            case CONDITIONAL:
2845            case WARRANT:
2846            case ENTRYEXIT:
2847                testType = _variableStateBox.getItemAt(_variableStateBox.getSelectedIndex());
2848                break;
2849            case MEMORY:
2850                testType = _variableCompareTypeBox.getItemAt(_variableCompareTypeBox.getSelectedIndex());
2851                break;
2852            case CLOCK:
2853                testType = Conditional.Type.FAST_CLOCK_RANGE;
2854                break;
2855            case OBLOCK:
2856                testType = Conditional.Type.BLOCK_STATUS_EQUALS;
2857                break;
2858            default:
2859                JmriJOptionPane.showMessageDialog(_editLogixFrame,
2860                        Bundle.getMessage("ErrorVariableType"), Bundle.getMessage("ErrorTitle"), // NOI18N
2861                        JmriJOptionPane.ERROR_MESSAGE);
2862                return false;
2863        }
2864        _curVariable.setType(testType);
2865        log.debug("validateVariable: itemType= {}, testType= {}", itemType, testType);  // NOI18N
2866        switch (itemType) {
2867            case SENSOR:
2868                name = validateSensorReference(name);
2869                if (name == null) {
2870                    return false;
2871                }
2872                break;
2873            case TURNOUT:
2874                name = validateTurnoutReference(name);
2875                if (name == null) {
2876                    return false;
2877                }
2878                break;
2879            case CONDITIONAL:
2880                name = validateConditionalReference(name);
2881                if (name == null) {
2882                    return false;
2883                }
2884                _curVariable.setName(name);
2885                Conditional c = _conditionalManager.getBySystemName(name);
2886                if (c == null) {
2887                    return false;
2888                }
2889                String uName = c.getUserName();
2890                if (uName == null || uName.isEmpty()) {
2891                    _curVariable.setGuiName(c.getSystemName());
2892                } else {
2893                    _curVariable.setGuiName(uName);
2894                }
2895                break;
2896            case LIGHT:
2897                name = validateLightReference(name);
2898                if (name == null) {
2899                    return false;
2900                }
2901                break;
2902            case MEMORY:
2903                name = validateMemoryReference(name);
2904                if (name == null) {
2905                    return false;
2906                }
2907                String name2 = _variableData1Field.getText();
2908                if ((testType == Conditional.Type.MEMORY_COMPARE)
2909                        || (testType == Conditional.Type.MEMORY_COMPARE_INSENSITIVE)) {
2910                    name2 = validateMemoryReference(name2);
2911                    if (name2 == null) {
2912                        return false;
2913                    }
2914                }
2915                _curVariable.setDataString(name2);
2916                _curVariable.setNum1(_variableCompareOpBox.getSelectedIndex() + 1);
2917                break;
2918            case CLOCK:
2919                int beginTime = parseTime(_variableData1Field.getText());
2920                if (beginTime < 0) {
2921                    // parse error occurred - message has been sent
2922                    return (false);
2923                }
2924                int endTime = parseTime(_variableData2Field.getText());
2925                if (endTime < 0) {
2926                    return (false);
2927                }
2928                // set beginning and end time (minutes since midnight)
2929                _curVariable.setNum1(beginTime);
2930                _curVariable.setNum2(endTime);
2931                name = "Clock";  // NOI18N
2932                break;
2933            case SIGNALHEAD:
2934                name = validateSignalHeadReference(name);
2935                if (name == null) {
2936                    return false;
2937                }
2938                if (testType == Conditional.Type.SIGNAL_HEAD_APPEARANCE_EQUALS) {
2939                    String appStr = (String) _variableSignalBox.getSelectedItem();
2940                    if (appStr != null) {
2941                        Conditional.Type type = ConditionalVariable.stringToVariableTest(appStr);
2942                        if (type == Conditional.Type.ERROR) {
2943                           JmriJOptionPane.showMessageDialog(_editLogixFrame, Bundle.getMessage("ErrorAppearance"), Bundle.getMessage("ErrorTitle"), // NOI18N
2944                                    JmriJOptionPane.ERROR_MESSAGE);
2945                            return false;
2946                        }
2947                        _curVariable.setType(type);
2948                        _curVariable.setDataString(appStr);
2949                        log.debug("SignalHead \"{}\"of type '{}' _variableSignalBox.getSelectedItem()= {}", name, testType, _variableSignalBox.getSelectedItem()); // NOI18N
2950                    } else {
2951                        log.warn("null selection in _variableSignalBox");
2952                    }
2953                }
2954                break;
2955            case SIGNALMAST:
2956                name = validateSignalMastReference(name);
2957                if (name == null) {
2958                    return false;
2959                }
2960                if (testType == Conditional.Type.SIGNAL_MAST_ASPECT_EQUALS) {
2961                    if (_variableSignalBox.getSelectedIndex() < 0) {
2962                       JmriJOptionPane.showMessageDialog(_editLogixFrame,
2963                                Bundle.getMessage("ErrorAspect"), Bundle.getMessage("ErrorTitle"), // NOI18N
2964                                JmriJOptionPane.ERROR_MESSAGE);
2965                        return false;
2966                    }
2967                    // save the selected aspect for comparison
2968                    _curVariable.setDataString((String) _variableSignalBox.getSelectedItem());
2969                    //                _curVariable.setType(ConditionalVariable.stringToVariableTest(appStr));
2970                }
2971                break;
2972            case WARRANT:
2973                name = validateWarrantReference(name);
2974                if (name == null) {
2975                    return false;
2976                }
2977                break;
2978            case OBLOCK:
2979                name = validateOBlockReference(name);
2980                if (name == null) {
2981                    return false;
2982                }
2983                String stri18n = (String) _variableStateBox.getSelectedItem();
2984                if (stri18n != null) {
2985                    _curVariable.setDataString(OBlock.getSystemStatusName(stri18n));
2986                    log.debug("OBlock \"{}\"of type '{}' _variableSignalBox.getSelectedItem()= {}", name, testType, _variableSignalBox.getSelectedItem()); // NOI18N
2987                }
2988                break;
2989            case ENTRYEXIT:
2990                name = validateEntryExitReference(name);
2991                if (name == null) {
2992                    return false;
2993                }
2994                break;
2995            default:
2996               JmriJOptionPane.showMessageDialog(_editLogixFrame,
2997                        Bundle.getMessage("ErrorVariableType"), Bundle.getMessage("ErrorTitle"), // NOI18N
2998                        JmriJOptionPane.ERROR_MESSAGE);
2999                return false;
3000        }
3001        _curVariable.setName(name);
3002        boolean result = _curVariable.evaluate();
3003        log.debug("State Variable \"{}\" of type '{}' state= {} type= {}",
3004                name, testType.getTestTypeString(),
3005                result, _curVariable.getType());  // NOI18N
3006        if (_curVariable.getType() == Conditional.Type.NONE) {
3007           JmriJOptionPane.showMessageDialog(_editLogixFrame,
3008                    Bundle.getMessage("ErrorVariableState"), Bundle.getMessage("ErrorTitle"), // NOI18N
3009                    JmriJOptionPane.ERROR_MESSAGE);
3010            return false;
3011        }
3012        return (true);
3013    }
3014
3015    /**
3016     * Update the variable operation. If a change has occurred, also update the
3017     * antecedent and antecedent tree node.
3018     */
3019    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
3020    void updateVariableOperator() {
3021        Operator oldOper = _curVariable.getOpern();
3022        if (_curNodeRow > 0) {
3023            if (_variableOperBox.getSelectedIndex() == 0) {
3024                _curVariable.setOpern(Conditional.Operator.AND);
3025            } else {
3026                _curVariable.setOpern(Conditional.Operator.OR);
3027            }
3028        } else {
3029            _curVariable.setOpern(Conditional.Operator.NONE);
3030        }
3031        if (_curVariable.getOpern() != oldOper) {
3032            makeAntecedent();
3033            _curConditional.setLogicType(_logicType, _antecedent); // non-localized
3034            ConditionalTreeNode antLeaf = (ConditionalTreeNode) ((ConditionalTreeNode) _curNode.getParent()).getPreviousSibling();
3035            antLeaf.setText(buildNodeText("Antecedent", _curConditional, 0));  // NOI18N
3036            _cdlModel.nodeChanged(antLeaf);
3037        }
3038    }
3039
3040    /**
3041     * Update the variable negation. If a change has occurred, also update the
3042     * antecedent and antecedent tree node.
3043     */
3044    @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "Except for the root node, all nodes are ConditionalTreeNode")  // NOI18N
3045    void updateVariableNegation() {
3046        boolean state = _curVariable.isNegated();
3047        if (_variableNegated.isSelected()) {
3048            _curVariable.setNegation(true);
3049        } else {
3050            _curVariable.setNegation(false);
3051        }
3052        if (_curVariable.isNegated() != state) {
3053            makeAntecedent();
3054            _curConditional.setLogicType(_logicType, _antecedent); // non-localized
3055            ConditionalTreeNode antLeaf = (ConditionalTreeNode) ((ConditionalTreeNode) _curNode.getParent()).getPreviousSibling();
3056            antLeaf.setText(buildNodeText("Antecedent", _curConditional, 0));
3057            _cdlModel.nodeChanged(antLeaf);
3058        }
3059    }
3060
3061    /**
3062     * Update the conditional variable list and refresh the local copy.
3063     * The parent Logix is de-activated and re-activated. This ensures
3064     * that listeners are properly handled.
3065     * @since 4.11.2
3066     */
3067    void updateVariableList() {
3068        _curLogix.deActivateLogix();
3069        _curConditional.setStateVariables(_variableList);
3070        _variableList = _curConditional.getCopyOfStateVariables();
3071        _curLogix.activateLogix();
3072    }
3073
3074    // ------------ Variable detail listeners ------------
3075
3076    transient ActionListener variableSignalHeadNameListener = new ActionListener() {
3077        @Override
3078        public void actionPerformed(ActionEvent e) {
3079            // fired when signal head name changes, but only
3080            // while in signal head mode
3081            log.debug("variableSignalHeadNameListener fires; _variableNameField : {}", _variableNameField.getText().trim());  // NOI18N
3082            loadJComboBoxWithHeadAppearances(_variableSignalBox, _variableNameField.getText().trim());
3083        }
3084    };
3085
3086    transient ActionListener variableSignalMastNameListener = new ActionListener() {
3087        @Override
3088        public void actionPerformed(ActionEvent e) {
3089            // fired when signal mast name changes, but only
3090            // while in signal mast mode
3091            log.debug("variableSignalMastNameListener fires; _variableNameField : {}", _variableNameField.getText().trim());  // NOI18N
3092            loadJComboBoxWithMastAspects(_variableSignalBox, _variableNameField.getText().trim());
3093        }
3094    };
3095
3096    transient ActionListener variableSignalTestStateListener = new ActionListener() {
3097        @Override
3098        public void actionPerformed(ActionEvent e) {
3099            log.debug("variableSignalTestStateListener fires; _variableItemBox.getSelectedIndex()= \"{}\" _variableStateBox.getSelectedIndex()= \"{}\"", // NOI18N
3100                    _variableItemBox.getSelectedIndex(),
3101                    _variableStateBox.getSelectedIndex());
3102
3103            Conditional.ItemType itemType = _variableItemBox.getItemAt(_variableItemBox.getSelectedIndex());
3104
3105            if (_variableStateBox.getSelectedIndex() == 1) {
3106                if (itemType == Conditional.ItemType.SIGNALHEAD) {
3107                    loadJComboBoxWithHeadAppearances(_variableSignalBox, _variableNameField.getText().trim());
3108                    _detailGrid.setVisible(false);
3109                    makeDetailGrid("SignalAspectVariable");  // NOI18N
3110                } else if (itemType == Conditional.ItemType.SIGNALMAST) {
3111                    loadJComboBoxWithMastAspects(_variableSignalBox, _variableNameField.getText().trim());
3112                    _detailGrid.setVisible(false);
3113                    makeDetailGrid("SignalAspectVariable");  // NOI18N
3114                } else {
3115                    _detailGrid.setVisible(false);
3116                    makeDetailGrid("StandardVariable");  // NOI18N
3117                }
3118            } else {
3119                _detailGrid.setVisible(false);
3120                makeDetailGrid("StandardVariable");  // NOI18N
3121            }
3122        }
3123    };
3124
3125    transient ActionListener selectLogixBoxListener = new ActionListener() {
3126        @Override
3127        public void actionPerformed(ActionEvent e) {
3128            String lgxItem = (String) _selectLogixBox.getSelectedItem();
3129            if (lgxItem != null) {
3130                String lgxName = _selectLogixMap.get(lgxItem);
3131                if (lgxName != null) {
3132                    loadSelectConditionalBox(lgxName);
3133                }
3134            }
3135        }
3136    };
3137
3138    transient ActionListener selectConditionalBoxListener = new ActionListener() {
3139        @Override
3140        public void actionPerformed(ActionEvent e) {
3141            int cdlIndex = _selectConditionalBox.getSelectedIndex();
3142            if (cdlIndex > 0 && cdlIndex < _selectConditionalList.size()) {
3143                String cdlName = _selectConditionalList.get(cdlIndex);
3144                _variableNameField.setText(cdlName);
3145            }
3146        }
3147    };
3148
3149    transient ActionListener compareTypeBoxListener = new ActionListener() {
3150        @Override
3151        public void actionPerformed(ActionEvent e) {
3152            int selection = _variableCompareTypeBox.getSelectedIndex();
3153            compareTypeChanged(Conditional.Type.getMemoryItems().get(selection));
3154        }
3155    };
3156
3157    // ============ Edit Action Section ============
3158
3159    /**
3160     * Called once during class initialization to define the GUI objects. Where
3161     * possible, the combo box content is loaded.
3162     */
3163    void buildActionComponents() {
3164        // Item Type
3165        _actionItemBox = new JComboBox<>();
3166        for (Conditional.ItemType itemType : Conditional.ItemType.values()) {
3167            _actionItemBox.addItem(itemType);
3168        }
3169        JComboBoxUtil.setupComboBoxMaxRows(_actionItemBox);
3170        _actionItemBox.addActionListener(new ActionListener() {
3171            @Override
3172            public void actionPerformed(ActionEvent e) {
3173                Conditional.ItemType newActionItem =
3174                        _actionItemBox.getItemAt(_actionItemBox.getSelectedIndex());
3175                if (log.isDebugEnabled()) {
3176                    log.debug("_actionItemBox Listener: new = {}, curr = {}, row = {}",  // NOI18N
3177                            newActionItem, _curActionItem, _curNodeRow);
3178                }
3179                if (newActionItem != _curActionItem) {
3180                    if (_curNodeRow >= 0) {
3181                        _curAction = new DefaultConditionalAction();
3182                        _actionList.set(_curNodeRow, _curAction);
3183                    }
3184                    _curActionItem = newActionItem;
3185                }
3186                actionItemChanged(newActionItem);
3187            }
3188        });
3189
3190        // Item Name
3191        _actionNameField = new JTextField(20);
3192
3193        // Action Type Box
3194        _actionTypeBox = new JComboBox<>();
3195        JComboBoxUtil.setupComboBoxMaxRows(_actionTypeBox);
3196        _actionTypeBox.addItem(Conditional.Action.NONE);
3197
3198        // Action State Box
3199        _actionBox = new JComboBox<String>();
3200        _actionBox.addItem("");
3201
3202        // Short strings: Delay time, memory value/copy target
3203        _shortActionString = new JTextField(15);
3204
3205        // On Change / Trigger options
3206        _actionOptionBox = new JComboBox<String>();
3207
3208        // File Selector
3209        _actionSetButton = new JButton("..."); // "File" replaced by ...
3210        _actionSetButton.addActionListener(new ActionListener() {
3211            @Override
3212            public void actionPerformed(ActionEvent e) {
3213                validateAction();
3214                setFileLocation(e);
3215            }
3216        });
3217
3218        // File names, etc.
3219        _longActionString = new JTextField(30);
3220    }
3221
3222    // ------------ Make Action Edit Grid Panels ------------
3223
3224    /**
3225     * Create a one row grid with just the Action Item box. This is the base for
3226     * larger grids as well as the initial grid for new Actions.
3227     *
3228     * @param c the constraints object used for the grid construction
3229     */
3230    void makeEmptyActionGrid(GridBagConstraints c) {
3231        // Action item box
3232        c.gridy = 0;
3233        c.gridx = 0;
3234        c.anchor = java.awt.GridBagConstraints.EAST;
3235        JLabel row0Label = new JLabel(Bundle.getMessage("LabelActionItem"));  // NOI18N
3236        row0Label.setToolTipText(Bundle.getMessage("ActionItemHint"));  // NOI18N
3237        _gridPanel.add(row0Label, c);
3238        c.gridx = 1;
3239        c.anchor = java.awt.GridBagConstraints.WEST;
3240        _gridPanel.add(_actionItemBox, c);
3241    }
3242
3243    /**
3244     * Create the standard Name and Type rows.
3245     * The name field will be either a text field or a combo box.
3246     * The name field label is a variable to support run time changes.
3247     *
3248     * @param c The constraints object used for the grid construction
3249     * @param finalRow Controls whether the tigger combo box is included
3250     */
3251    void makeNameTypeActionGrid(GridBagConstraints c, boolean finalRow) {
3252        makeEmptyActionGrid(c);
3253
3254        Conditional.Action actionType = _curAction.getType();
3255        Conditional.ItemType itemType = actionType.getItemType();
3256
3257        // Name Field
3258        c.gridy = 1;
3259        c.gridx = 0;
3260        c.anchor = java.awt.GridBagConstraints.EAST;
3261        _gridPanel.add(_actionNameLabel, c);
3262        c.gridx = 1;
3263        c.anchor = java.awt.GridBagConstraints.WEST;
3264        if ((_selectionMode == SelectionMode.USECOMBO)
3265                && (itemType != Conditional.ItemType.AUDIO)) {
3266            _gridPanel.add(_comboNameBox, c);
3267        } else {
3268            _gridPanel.add(_actionNameField, c);
3269        }
3270
3271        // Action Type Box
3272        c.gridy = 2;
3273        c.gridx = 0;
3274        c.anchor = java.awt.GridBagConstraints.EAST;
3275        JLabel row5Label = new JLabel(Bundle.getMessage("LabelActionType"));  // NOI18N
3276        row5Label.setToolTipText(Bundle.getMessage("ActionTypeHint"));  // NOI18N
3277        _gridPanel.add(row5Label, c);
3278        c.gridx = 1;
3279        c.anchor = java.awt.GridBagConstraints.WEST;
3280        _gridPanel.add(_actionTypeBox, c);
3281
3282        if (itemType == Conditional.ItemType.NONE) {
3283            // Skip the change/trigger section for new Actions
3284            return;
3285        }
3286
3287        if (finalRow) {
3288            makeChangeTriggerActionGrid(c);
3289        }
3290    }
3291
3292    /**
3293     * Create the standard Type row.
3294     *
3295     * @param c The constraints object used for the grid construction
3296     * @param finalRow Controls whether the tigger combo box is included
3297     */
3298    void makeTypeActionGrid(GridBagConstraints c, boolean finalRow) {
3299        makeEmptyActionGrid(c);
3300
3301        Conditional.Action actionType = _curAction.getType();
3302        Conditional.ItemType itemType = actionType.getItemType();
3303
3304        // Action Type Box
3305        c.gridy = 1;
3306        c.gridx = 0;
3307        c.anchor = java.awt.GridBagConstraints.EAST;
3308        JLabel row5Label = new JLabel(Bundle.getMessage("LabelActionType"));  // NOI18N
3309        row5Label.setToolTipText(Bundle.getMessage("ActionTypeHint"));  // NOI18N
3310        _gridPanel.add(row5Label, c);
3311        c.gridx = 1;
3312        c.anchor = java.awt.GridBagConstraints.WEST;
3313        _gridPanel.add(_actionTypeBox, c);
3314
3315        if (itemType == Conditional.ItemType.NONE) {
3316            // Skip the change/trigger section for new Actions
3317            return;
3318        }
3319
3320        if (finalRow) {
3321            makeChangeTriggerActionGrid(c);
3322        }
3323    }
3324
3325    /**
3326     * Add the action box to the grid.
3327     *
3328     * @param c        The constraints object used for the grid construction
3329     * @param finalRow Controls whether the tigger combo box is included
3330     */
3331    void makeStandardActionGrid(GridBagConstraints c, boolean finalRow) {
3332        makeNameTypeActionGrid(c, false);
3333
3334        // Action Box
3335        c.gridy = 3;
3336        c.gridx = 0;
3337        c.anchor = java.awt.GridBagConstraints.EAST;
3338        _gridPanel.add(_actionBoxLabel, c);
3339        c.gridx = 1;
3340        c.anchor = java.awt.GridBagConstraints.WEST;
3341        _gridPanel.add(_actionBox, c);
3342
3343        if (finalRow) {
3344            makeChangeTriggerActionGrid(c);
3345        }
3346    }
3347
3348    /**
3349     * Add the short name field to the grid.
3350     *
3351     * @param c          The constraints object used for the grid construction
3352     * @param includeBox Controls whether the normal action type combo box is
3353     *                   included
3354     */
3355    void makeShortFieldActionGrid(GridBagConstraints c, boolean includeBox) {
3356        if (includeBox) {
3357            makeStandardActionGrid(c, false);
3358        } else {
3359            makeNameTypeActionGrid(c, false);
3360        }
3361
3362        // Add the short text field
3363        c.gridy = 4;
3364        c.gridx = 0;
3365        c.anchor = java.awt.GridBagConstraints.EAST;
3366        _gridPanel.add(_shortActionLabel, c);
3367        c.gridx = 1;
3368        c.anchor = java.awt.GridBagConstraints.WEST;
3369        _gridPanel.add(_shortActionString, c);
3370
3371        makeChangeTriggerActionGrid(c);
3372    }
3373
3374    /**
3375     * Just a short text field, no name field. Used by set clock and jython
3376     * command.
3377     *
3378     * @param c The constraints object used for the grid construction
3379     */
3380    void makeTypeShortActionGrid(GridBagConstraints c) {
3381        makeTypeActionGrid(c, false);
3382
3383        // Add the short text field
3384        c.gridy = 2;
3385        c.gridx = 0;
3386        c.anchor = java.awt.GridBagConstraints.EAST;
3387        _gridPanel.add(_shortActionLabel, c);
3388        c.gridx = 1;
3389        c.anchor = java.awt.GridBagConstraints.WEST;
3390        _gridPanel.add(_shortActionString, c);
3391
3392        makeChangeTriggerActionGrid(c);
3393    }
3394
3395    /**
3396     * Add the file selection components.
3397     *
3398     * @param c The constraints object used for the grid construction
3399     */
3400    void makeFileActionGrid(GridBagConstraints c) {
3401        makeTypeActionGrid(c, false);
3402
3403        // Add the file selecttor
3404        c.gridy = 2;
3405        c.gridx = 0;
3406        c.anchor = java.awt.GridBagConstraints.EAST;
3407        _gridPanel.add(_shortActionLabel, c);
3408        c.gridx = 1;
3409        c.anchor = java.awt.GridBagConstraints.WEST;
3410        _gridPanel.add(_actionSetButton, c);
3411
3412        c.gridwidth = 2;    // span both columns
3413        // Add the long text field
3414        c.gridy = 3;
3415        c.gridx = 0;
3416        c.anchor = java.awt.GridBagConstraints.CENTER;
3417        _gridPanel.add(_longActionString, c);
3418        c.gridwidth = 1;
3419
3420        makeChangeTriggerActionGrid(c);
3421    }
3422
3423    /**
3424     * Add the change/trigger box the grid. This is the last item in an Action
3425     * and is usually called from one of the other entry points.
3426     *
3427     * @param c The constraints object used for the grid construction
3428     */
3429    void makeChangeTriggerActionGrid(GridBagConstraints c) {
3430        // Action item box
3431        c.gridy = 9;
3432        c.gridx = 0;
3433        c.anchor = java.awt.GridBagConstraints.EAST;
3434        JLabel row0Label = new JLabel(Bundle.getMessage("LabelActionOption"));  // NOI18N
3435        row0Label.setToolTipText(Bundle.getMessage("ActionOptionHint"));  // NOI18N
3436        _gridPanel.add(row0Label, c);
3437        c.gridx = 1;
3438        c.anchor = java.awt.GridBagConstraints.WEST;
3439        _gridPanel.add(_actionOptionBox, c);
3440    }
3441
3442    // ------------ Main Action methods ------------
3443
3444    /**
3445     * Set display to show current action (curAction) parameters.
3446     */
3447    void initializeActionVariables() {
3448        Conditional.Action actionType = _curAction.getType();
3449        Conditional.ItemType itemType = actionType.getItemType();
3450        log.debug("initializeActionVariables: itemType= {}, actionType= {}", itemType, actionType);  // NOI18N
3451        _actionItemBox.setSelectedItem(itemType);
3452        _actionNameField.setText(_curAction.getDeviceName());
3453        switch (itemType) {
3454            case NONE:
3455                _actionNameField.setText("");
3456                break;
3457
3458            case SENSOR:
3459                _actionTypeBox.setSelectedItem(actionType);
3460                if ((actionType == Conditional.Action.RESET_DELAYED_SENSOR)
3461                        || (actionType == Conditional.Action.DELAYED_SENSOR)) {
3462                    _shortActionString.setText(_curAction.getActionString());
3463                }
3464                if (actionType == Conditional.Action.SET_SENSOR
3465                        || actionType == Conditional.Action.DELAYED_SENSOR
3466                        || actionType == Conditional.Action.RESET_DELAYED_SENSOR) {
3467                    if (_curAction.getActionData() == Sensor.ACTIVE) {
3468                        _actionBox.setSelectedIndex(0);
3469                    } else if (_curAction.getActionData() == Sensor.INACTIVE) {
3470                        _actionBox.setSelectedIndex(1);
3471                    } else if (_curAction.getActionData() == Route.TOGGLE) {
3472                        _actionBox.setSelectedIndex(2);
3473                    }
3474                }
3475                break;
3476
3477            case TURNOUT:
3478                _actionTypeBox.setSelectedItem(actionType);
3479                if ((actionType == Conditional.Action.RESET_DELAYED_TURNOUT)
3480                        || (actionType == Conditional.Action.DELAYED_TURNOUT)) {
3481                    _shortActionString.setText(_curAction.getActionString());
3482                }
3483                if ((actionType == Conditional.Action.SET_TURNOUT)
3484                        || (actionType == Conditional.Action.RESET_DELAYED_TURNOUT)
3485                        || (actionType == Conditional.Action.DELAYED_TURNOUT)) {
3486                    if (_curAction.getActionData() == Turnout.CLOSED) {
3487                        _actionBox.setSelectedIndex(0);
3488                    } else if (_curAction.getActionData() == Turnout.THROWN) {
3489                        _actionBox.setSelectedIndex(1);
3490                    } else if (_curAction.getActionData() == Route.TOGGLE) {
3491                        _actionBox.setSelectedIndex(2);
3492                    }
3493                } else if (actionType == Conditional.Action.LOCK_TURNOUT) {
3494                    if (_curAction.getActionData() == Turnout.UNLOCKED) {
3495                        _actionBox.setSelectedIndex(0);
3496                    } else if (_curAction.getActionData() == Turnout.LOCKED) {
3497                        _actionBox.setSelectedIndex(1);
3498                    } else if (_curAction.getActionData() == Route.TOGGLE) {
3499                        _actionBox.setSelectedIndex(2);
3500                    }
3501                }
3502                break;
3503
3504            case LIGHT:
3505                _actionTypeBox.setSelectedItem(actionType);
3506                if (actionType == Conditional.Action.SET_LIGHT) {
3507                    if (_curAction.getActionData() == Light.ON) {
3508                        _actionBox.setSelectedIndex(0);
3509                    } else if (_curAction.getActionData() == Light.OFF) {
3510                        _actionBox.setSelectedIndex(1);
3511                    } else if (_curAction.getActionData() == Route.TOGGLE) {
3512                        _actionBox.setSelectedIndex(2);
3513                    }
3514                } else if ((actionType == Conditional.Action.SET_LIGHT_INTENSITY)
3515                        || (actionType == Conditional.Action.SET_LIGHT_TRANSITION_TIME)) {
3516                    _shortActionString.setText(_curAction.getActionString());
3517                }
3518                break;
3519
3520            case SIGNALHEAD:
3521                _actionTypeBox.setSelectedItem(actionType);
3522                if (actionType == Conditional.Action.SET_SIGNAL_APPEARANCE) {
3523                    loadJComboBoxWithHeadAppearances(_actionBox, _actionNameField.getText().trim());
3524                }
3525                break;
3526
3527            case CLOCK:
3528                _actionTypeBox.setSelectedItem(actionType);
3529                if (actionType == Conditional.Action.SET_FAST_CLOCK_TIME) {
3530                    int time = _curAction.getActionData();
3531                    _longActionString.setText(formatTime(time / 60, time - ((time / 60) * 60)));
3532                    _actionNameField.setText("");
3533                }
3534                break;
3535
3536            case MEMORY:
3537                _actionTypeBox.setSelectedItem(actionType);
3538                _shortActionString.setText(_curAction.getActionString());
3539                break;
3540
3541            case WARRANT:
3542                _actionTypeBox.setSelectedItem(actionType);
3543                if (actionType == Conditional.Action.CONTROL_TRAIN) {
3544                    switch (_curAction.getActionData()) {
3545                        case Warrant.HALT:
3546                            _actionBox.setSelectedIndex(0);
3547                            break;
3548                        case Warrant.RESUME:
3549                            _actionBox.setSelectedIndex(1);
3550                            break;
3551                        case Warrant.RETRY_FWD:
3552                            _actionBox.setSelectedIndex(2);
3553                            break;
3554                        case Warrant.SPEED_UP:
3555                            _actionBox.setSelectedIndex(3);
3556                            break;
3557                        case Warrant.STOP:
3558                            _actionBox.setSelectedIndex(4);
3559                            break;
3560                        case Warrant.ESTOP:
3561                            _actionBox.setSelectedIndex(5);
3562                            break;
3563                        case Warrant.ABORT:
3564                            _actionBox.setSelectedIndex(6);
3565                            break;
3566                        default:
3567                            log.warn("Unexpected _curAction.getActionData() of {}", _curAction.getActionData());  // NOI18N
3568                    }
3569                } else if (actionType == Conditional.Action.SET_TRAIN_ID
3570                        || actionType == Conditional.Action.SET_TRAIN_NAME) {
3571                    _shortActionString.setText(_curAction.getActionString());
3572                }
3573                break;
3574
3575            case OBLOCK:
3576                _actionTypeBox.setSelectedItem(actionType);
3577                if (actionType == Conditional.Action.SET_BLOCK_VALUE
3578                        || actionType == Conditional.Action.SET_TRAIN_NAME
3579                        || actionType == Conditional.Action.GET_TRAIN_LOCATION) {
3580                    _shortActionString.setText(_curAction.getActionString());
3581                }
3582                break;
3583
3584            case ENTRYEXIT:
3585                _actionNameField.setText(_curAction.getBean().getUserName());
3586                _actionTypeBox.setSelectedItem(actionType);
3587                break;
3588
3589            case AUDIO:
3590                _actionTypeBox.setSelectedItem(actionType);
3591                if (actionType == Conditional.Action.PLAY_SOUND) {
3592                    _longActionString.setText(_curAction.getActionString());
3593                } else if (actionType == Conditional.Action.CONTROL_AUDIO) {
3594                    switch (_curAction.getActionData()) {
3595                        case Audio.CMD_PLAY:
3596                            _actionBox.setSelectedIndex(0);
3597                            break;
3598                        case Audio.CMD_STOP:
3599                            _actionBox.setSelectedIndex(1);
3600                            break;
3601                        case Audio.CMD_PLAY_TOGGLE:
3602                            _actionBox.setSelectedIndex(2);
3603                            break;
3604                        case Audio.CMD_PAUSE:
3605                            _actionBox.setSelectedIndex(3);
3606                            break;
3607                        case Audio.CMD_RESUME:
3608                            _actionBox.setSelectedIndex(4);
3609                            break;
3610                        case Audio.CMD_PAUSE_TOGGLE:
3611                            _actionBox.setSelectedIndex(5);
3612                            break;
3613                        case Audio.CMD_REWIND:
3614                            _actionBox.setSelectedIndex(6);
3615                            break;
3616                        case Audio.CMD_FADE_IN:
3617                            _actionBox.setSelectedIndex(7);
3618                            break;
3619                        case Audio.CMD_FADE_OUT:
3620                            _actionBox.setSelectedIndex(8);
3621                            break;
3622                        case Audio.CMD_RESET_POSITION:
3623                            _actionBox.setSelectedIndex(9);
3624                            break;
3625                        default:
3626                            log.warn("Unexpected _curAction.getActionData() of {}", _curAction.getActionData());  // NOI18N
3627                            break;
3628                    }
3629                }
3630                break;
3631
3632            case SCRIPT:
3633                _actionTypeBox.setSelectedItem(actionType);
3634                if (actionType == Conditional.Action.RUN_SCRIPT) {
3635                    _longActionString.setText(_curAction.getActionString());
3636                } else if (actionType == Conditional.Action.JYTHON_COMMAND) {
3637                    _shortActionString.setText(_curAction.getActionString());
3638                }
3639                break;
3640
3641            case SIGNALMAST:
3642            case LOGIX:
3643            case OTHER: // ACTION_TRIGGER_ROUTE
3644                _actionTypeBox.setSelectedItem(actionType);
3645                break;
3646
3647            default:
3648                log.error("Unhandled type: {}", itemType);  // NOI18N
3649                break;
3650        }
3651        _actionOptionBox.setSelectedIndex(_curAction.getOption() - 1);
3652    }
3653
3654    /**
3655     * Respond to a change in an Action Type comboBox.
3656     * <p>
3657     * Set components visible for the selected type.
3658     *
3659     * @param type index of the newly selected Action type
3660     */
3661    void actionItemChanged(Conditional.ItemType type) {
3662        Conditional.Action actionType = _curAction.getType();
3663        log.debug("actionItemChanged: itemType= {}, actionType= {}", type, actionType);  // NOI18N
3664        _detailGrid.setVisible(false);
3665        _actionTypeBox.removeActionListener(_actionTypeListener);
3666        _shortActionString.setText("");
3667        _longActionString.setText("");
3668        _actionTypeBox.removeAllItems();
3669        _actionTypeBox.addItem(Conditional.Action.NONE);
3670        _actionBox.removeAllItems();
3671        Conditional.ItemType itemType = actionType.getItemType();
3672        if (type != Conditional.ItemType.NONE) {  // actionItem listener choice overrides current item
3673            itemType = type;
3674        }
3675        if (itemType != actionType.getItemType()) {
3676            actionType = Conditional.Action.NONE;    // chosen item type does not support action type
3677        }
3678
3679        _actionNameField.removeActionListener(actionSignalHeadNameListener);
3680        _actionNameField.removeActionListener(actionSignalMastNameListener);
3681
3682        if (_comboNameBox != null) {
3683            for (ActionListener item : _comboNameBox.getActionListeners()) {
3684                _comboNameBox.removeActionListener(item);
3685            }
3686            _comboNameBox.removeFocusListener(detailFocusEvent);
3687        }
3688        setPickWindow("Action", itemType);  // NOI18N
3689
3690        switch (itemType) {
3691            case NONE:
3692                makeDetailGrid("EmptyAction");  // NOI18N
3693                break;
3694
3695            case TURNOUT:
3696                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintTurnout"));  // NOI18N
3697                String turnoutGrid = "NameTypeAction";  // NOI18N
3698                boolean delayTurnout = false;
3699
3700                for (Conditional.Action action : Conditional.Action.getTurnoutItems()) {
3701                    _actionTypeBox.addItem(action);
3702                }
3703
3704                if ((actionType == Conditional.Action.RESET_DELAYED_TURNOUT)
3705                        || (actionType == Conditional.Action.DELAYED_TURNOUT)) {
3706                    delayTurnout = true;
3707                    _shortActionLabel.setText(Bundle.getMessage("LabelDelayTime"));  // NOI18N
3708                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintDelayedTurnout"));  // NOI18N
3709                }
3710                if ((actionType == Conditional.Action.SET_TURNOUT)
3711                        || (actionType == Conditional.Action.RESET_DELAYED_TURNOUT)
3712                        || (actionType == Conditional.Action.DELAYED_TURNOUT)) {
3713                    turnoutGrid = (delayTurnout) ? "ShortFieldAction" : "StandardAction";  // NOI18N
3714                    _actionBoxLabel.setText(Bundle.getMessage("LabelActionTurnout"));  // NOI18N
3715                    _actionBoxLabel.setToolTipText(Bundle.getMessage("TurnoutSetHint"));  // NOI18N
3716                    _actionBox.addItem(Bundle.getMessage("TurnoutStateClosed"));  // NOI18N
3717                    _actionBox.addItem(Bundle.getMessage("TurnoutStateThrown"));  // NOI18N
3718                    _actionBox.addItem(Bundle.getMessage("Toggle"));  // NOI18N
3719                } else if (actionType == Conditional.Action.LOCK_TURNOUT) {
3720                    turnoutGrid = (delayTurnout) ? "ShortFieldAction" : "StandardAction";  // NOI18N
3721                    _actionBoxLabel.setText(Bundle.getMessage("LabelActionLock"));  // NOI18N
3722                    _actionBoxLabel.setToolTipText(Bundle.getMessage("LockSetHint"));  // NOI18N
3723                    _actionBox.addItem(Bundle.getMessage("TurnoutUnlock"));  // NOI18N
3724                    _actionBox.addItem(Bundle.getMessage("TurnoutLock"));  // NOI18N
3725                    _actionBox.addItem(Bundle.getMessage("Toggle"));  // NOI18N
3726                } else if ((actionType == Conditional.Action.CANCEL_TURNOUT_TIMERS)
3727                        || (actionType == Conditional.Action.NONE)) {
3728                    turnoutGrid = "NameTypeActionFinal";  // NOI18N
3729                }
3730
3731                setActionNameBox(itemType);
3732                makeDetailGrid(turnoutGrid);
3733                break;
3734
3735            case SENSOR:
3736                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintSensor"));  // NOI18N
3737                String sensorGrid = "NameTypeAction";  // NOI18N
3738                boolean delaySensor = false;
3739
3740                for (Conditional.Action action : Conditional.Action.getSensorItems()) {
3741                    _actionTypeBox.addItem(action);
3742                }
3743                if ((actionType == Conditional.Action.RESET_DELAYED_SENSOR)
3744                        || (actionType == Conditional.Action.DELAYED_SENSOR)) {
3745                    delaySensor = true;
3746                    _shortActionLabel.setText(Bundle.getMessage("LabelDelayTime"));  // NOI18N
3747                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintDelayedSensor"));  // NOI18N
3748                }
3749                if ((actionType == Conditional.Action.SET_SENSOR)
3750                        || (actionType == Conditional.Action.RESET_DELAYED_SENSOR)
3751                        || (actionType == Conditional.Action.DELAYED_SENSOR)) {
3752                    sensorGrid = (delaySensor) ? "ShortFieldAction" : "StandardAction";  // NOI18N
3753                    _actionBoxLabel.setText(Bundle.getMessage("LabelActionSensor"));  // NOI18N
3754                    _actionBoxLabel.setToolTipText(Bundle.getMessage("SensorSetHint"));  // NOI18N
3755                    _actionBox.addItem(Bundle.getMessage("SensorStateActive"));  // NOI18N
3756                    _actionBox.addItem(Bundle.getMessage("SensorStateInactive"));  // NOI18N
3757                    _actionBox.addItem(Bundle.getMessage("Toggle"));  // NOI18N
3758                } else if ((actionType == Conditional.Action.CANCEL_SENSOR_TIMERS)
3759                        || (actionType == Conditional.Action.NONE)) {
3760                    sensorGrid = "NameTypeActionFinal";  // NOI18N
3761                }
3762
3763                setActionNameBox(itemType);
3764                makeDetailGrid(sensorGrid);
3765                break;
3766
3767            case SIGNALHEAD:
3768                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintSignal"));  // NOI18N
3769                String signalHeadGrid = "NameTypeAction";  // NOI18N
3770                _actionNameField.addActionListener(actionSignalHeadNameListener);
3771
3772                for (Conditional.Action action : Conditional.Action.getSignalHeadItems()) {
3773                    _actionTypeBox.addItem(action);
3774                }
3775
3776                if (actionType == Conditional.Action.SET_SIGNAL_APPEARANCE) {
3777                    signalHeadGrid = "StandardAction";  // NOI18N
3778                    _actionBoxLabel.setText(Bundle.getMessage("LabelActionSignal"));  // NOI18N
3779                    _actionBoxLabel.setToolTipText(Bundle.getMessage("SignalSetHint"));  // NOI18N
3780                    loadJComboBoxWithHeadAppearances(_actionBox, _actionNameField.getText().trim());
3781                    _actionBox.setSelectedItem(_curAction.getActionDataString());
3782                } else if (actionType != Conditional.Action.NONE) {
3783                    signalHeadGrid = "NameTypeActionFinal";  // NOI18N
3784                }
3785
3786                setActionNameBox(itemType);
3787                makeDetailGrid(signalHeadGrid);
3788                break;
3789
3790            case SIGNALMAST:
3791                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintSignalMast"));  // NOI18N
3792                String signalMastGrid = "NameTypeAction";  // NOI18N
3793                _actionNameField.addActionListener(actionSignalMastNameListener);
3794
3795                for (Conditional.Action action : Conditional.Action.getSignalMastItems()) {
3796                    _actionTypeBox.addItem(action);
3797                }
3798
3799                if (actionType == Conditional.Action.SET_SIGNALMAST_ASPECT) {
3800                    signalMastGrid = "StandardAction";  // NOI18N
3801                    _actionBoxLabel.setText(Bundle.getMessage("LabelSignalAspect"));  // NOI18N
3802                    _actionBoxLabel.setToolTipText(Bundle.getMessage("SignalMastSetHint"));  // NOI18N
3803                    loadJComboBoxWithMastAspects(_actionBox, _actionNameField.getText().trim());
3804                    _actionBox.setSelectedItem(_curAction.getActionDataString());
3805                } else if (actionType != Conditional.Action.NONE) {
3806                    signalMastGrid = "NameTypeActionFinal";  // NOI18N
3807                }
3808
3809                setActionNameBox(itemType);
3810                makeDetailGrid(signalMastGrid);
3811                break;
3812
3813            case LIGHT:
3814                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintLight"));  // NOI18N
3815                String lightGrid = "NameTypeAction";  // NOI18N
3816
3817                for (Conditional.Action action : Conditional.Action.getLightItems()) {
3818                    _actionTypeBox.addItem(action);
3819                }
3820
3821                if (actionType == Conditional.Action.SET_LIGHT_INTENSITY) {
3822                    lightGrid = "ShortFieldNoBoxAction";  // NOI18N
3823                    _shortActionLabel.setText(Bundle.getMessage("LabelLightIntensity"));  // NOI18N
3824                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintLightIntensity"));  // NOI18N
3825                } else if (actionType == Conditional.Action.SET_LIGHT_TRANSITION_TIME) {
3826                    lightGrid = "ShortFieldNoBoxAction";  // NOI18N
3827                    _shortActionLabel.setText(Bundle.getMessage("LabelTransitionTime"));  // NOI18N
3828                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintLightTransitionTime"));  // NOI18N
3829                } else if (actionType == Conditional.Action.SET_LIGHT) {
3830                    lightGrid = "StandardAction";  // NOI18N
3831                    _actionBoxLabel.setText(Bundle.getMessage("LabelActionLight"));  // NOI18N
3832                    _actionBoxLabel.setToolTipText(Bundle.getMessage("LightSetHint"));  // NOI18N
3833                    _actionBox.addItem(Bundle.getMessage("LightOn"));  // NOI18N
3834                    _actionBox.addItem(Bundle.getMessage("LightOff")); // NOI18N
3835                    _actionBox.addItem(Bundle.getMessage("Toggle"));   // NOI18N
3836                }
3837
3838                setActionNameBox(itemType);
3839                makeDetailGrid(lightGrid);
3840                break;
3841
3842            case MEMORY:
3843                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintMemory"));  // NOI18N
3844                String memoryGrid = "NameTypeAction";  // NOI18N
3845
3846                for (Conditional.Action action : Conditional.Action.getMemoryItems()) {
3847                    _actionTypeBox.addItem(action);
3848                }
3849
3850                if (actionType == Conditional.Action.COPY_MEMORY) {
3851                    memoryGrid = "ShortFieldNoBoxAction";  // NOI18N
3852                    _shortActionLabel.setText(Bundle.getMessage("LabelMemoryLocation"));  // NOI18N
3853                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintToMemory"));  // NOI18N
3854                } else if (actionType == Conditional.Action.SET_MEMORY) {
3855                    memoryGrid = "ShortFieldNoBoxAction";  // NOI18N
3856                    _shortActionLabel.setText(Bundle.getMessage("LabelValue"));  // NOI18N
3857                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintMemory"));  // NOI18N
3858                }
3859
3860                setActionNameBox(itemType);
3861                makeDetailGrid(memoryGrid);
3862                break;
3863
3864            case CLOCK:
3865                String clockGrid = "TypeAction";  // NOI18N
3866
3867                for (Conditional.Action action : Conditional.Action.getClockItems()) {
3868                    _actionTypeBox.addItem(action);
3869                }
3870
3871                if (actionType == Conditional.Action.SET_FAST_CLOCK_TIME) {
3872                    clockGrid = "TypeShortAction";  // NOI18N
3873                    _shortActionLabel.setText(Bundle.getMessage("LabelSetTime"));  // NOI18N
3874                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintTime"));  // NOI18N
3875                } else if ((actionType == Conditional.Action.START_FAST_CLOCK)
3876                        || (actionType == Conditional.Action.STOP_FAST_CLOCK)) {
3877                    clockGrid = "TypeActionFinal";  // NOI18N
3878                }
3879
3880                makeDetailGrid(clockGrid);
3881                break;
3882
3883            case LOGIX:
3884                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintLogix"));  // NOI18N
3885                String logixGrid = "NameTypeAction";  // NOI18N
3886
3887                for (Conditional.Action action : Conditional.Action.getLogixItems()) {
3888                    _actionTypeBox.addItem(action);
3889                }
3890
3891                if ((actionType == Conditional.Action.ENABLE_LOGIX)
3892                        || (actionType == Conditional.Action.DISABLE_LOGIX)) {
3893                    logixGrid = "NameTypeActionFinal";  // NOI18N
3894                }
3895
3896                setActionNameBox(itemType);
3897                makeDetailGrid(logixGrid);
3898                break;
3899
3900            case WARRANT:
3901                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintWarrant"));  // NOI18N
3902                String warrantGrid = "NameTypeAction";  // NOI18N
3903
3904                for (Conditional.Action action : Conditional.Action.getWarrantItems()) {
3905                    _actionTypeBox.addItem(action);
3906                }
3907
3908                if (actionType == Conditional.Action.CONTROL_TRAIN) {
3909                    warrantGrid = "StandardAction";  // NOI18N
3910                    _actionBoxLabel.setText(Bundle.getMessage("LabelControlTrain"));  // NOI18N
3911                    _actionBoxLabel.setToolTipText(Bundle.getMessage("DataHintTrainControl"));  // NOI18N
3912                    _actionBox.addItem(Bundle.getMessage("WarrantHalt"));
3913                    _actionBox.addItem(Bundle.getMessage("WarrantResume"));
3914                    _actionBox.addItem(Bundle.getMessage("WarrantMoveToNext"));
3915                    _actionBox.addItem(Bundle.getMessage("WarrantSpeedUp"));
3916                    _actionBox.addItem(Bundle.getMessage("WarrantStop"));
3917                    _actionBox.addItem(Bundle.getMessage("WarantEStop"));
3918                    _actionBox.addItem(Bundle.getMessage("WarrantAbort"));
3919                } else if (actionType == Conditional.Action.SET_TRAIN_ID
3920                        || actionType == Conditional.Action.SET_TRAIN_NAME
3921                        || actionType == Conditional.Action.GET_TRAIN_LOCATION) {
3922                    warrantGrid = "ShortFieldNoBoxAction";  // NOI18N
3923                    if (actionType == Conditional.Action.SET_TRAIN_ID) {
3924                        _shortActionLabel.setText(Bundle.getMessage("LabelTrainId"));  // NOI18N
3925                        _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintTrainId"));  // NOI18N
3926                    } else if (actionType == Conditional.Action.SET_TRAIN_NAME) {
3927                        _shortActionLabel.setText(Bundle.getMessage("LabelTrainName"));  // NOI18N
3928                        _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintTrainName"));  // NOI18N
3929                    } else if (actionType == Conditional.Action.GET_TRAIN_LOCATION) {
3930                        _shortActionLabel.setText(Bundle.getMessage("LabelMemoryLocation"));  // NOI18N
3931                        _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintToMemory"));  // NOI18N
3932                    }
3933                }
3934                setActionNameBox(itemType);
3935                makeDetailGrid(warrantGrid);
3936                break;
3937
3938            case OBLOCK:
3939                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintOBlock"));  // NOI18N
3940                String oblockGrid = "NameTypeAction";  // NOI18N
3941
3942                for (Conditional.Action action : Conditional.Action.getOBlockItems()) {
3943                    _actionTypeBox.addItem(action);
3944                }
3945                if (actionType == Conditional.Action.SET_BLOCK_VALUE) {
3946                    _shortActionLabel.setText(Bundle.getMessage("LabelBlockValue"));  // NOI18N
3947                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintBlockValue"));  // NOI18N
3948                    oblockGrid = "ShortFieldNoBoxAction";  // NOI18N
3949                } else if(actionType == Conditional.Action.GET_BLOCK_TRAIN_NAME
3950                        || actionType == Conditional.Action.GET_BLOCK_WARRANT) {
3951                    _shortActionLabel.setText(Bundle.getMessage("LabelMemoryLocation"));  // NOI18N
3952                    _shortActionLabel.setToolTipText(Bundle.getMessage("DataHintToMemory"));  // NOI18N
3953                    oblockGrid = "ShortFieldNoBoxAction";  // NOI18N
3954                } else {
3955                    oblockGrid = "NameTypeActionFinal";  // NOI18N
3956                }
3957
3958                setActionNameBox(itemType);
3959                makeDetailGrid(oblockGrid);
3960                break;
3961
3962            case ENTRYEXIT:
3963                for (Conditional.Action action : Conditional.Action.getEntryExitItems()) {
3964                    _actionTypeBox.addItem(action);
3965                }
3966                setActionNameBox(itemType);
3967                makeDetailGrid("NameTypeActionFinal");
3968                break;
3969
3970            case AUDIO:
3971                _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintOBlock"));  // NOI18N
3972                String audioGrid = "TypeAction";  // NOI18N
3973
3974                for (Conditional.Action action : Conditional.Action.getAudioItems()) {
3975                    _actionTypeBox.addItem(action);
3976                }
3977
3978                if (actionType == Conditional.Action.PLAY_SOUND) {
3979                    audioGrid = "FileAction";  // NOI18N
3980                    _shortActionLabel.setText(Bundle.getMessage("LabelSelectFile"));  // NOI18N
3981                    _actionSetButton.setToolTipText(Bundle.getMessage("SetHintSound"));  // NOI18N
3982                } else if (actionType == Conditional.Action.CONTROL_AUDIO) {
3983                    audioGrid = "StandardAction";  // NOI18N
3984                    _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintAudio"));  // NOI18N
3985                    _actionBoxLabel.setText(Bundle.getMessage("LabelActionAudio"));  // NOI18N
3986                    _actionBoxLabel.setToolTipText(Bundle.getMessage("SetHintAudio"));  // NOI18N
3987                    _actionBox.addItem(Bundle.getMessage("AudioSourcePlay"));        // NOI18N
3988                    _actionBox.addItem(Bundle.getMessage("AudioSourceStop"));        // NOI18N
3989                    _actionBox.addItem(Bundle.getMessage("AudioSourcePlayToggle"));  // NOI18N
3990                    _actionBox.addItem(Bundle.getMessage("AudioSourcePause"));       // NOI18N
3991                    _actionBox.addItem(Bundle.getMessage("AudioSourceResume"));      // NOI18N
3992                    _actionBox.addItem(Bundle.getMessage("AudioSourcePauseToggle")); // NOI18N
3993                    _actionBox.addItem(Bundle.getMessage("AudioSourceRewind"));      // NOI18N
3994                    _actionBox.addItem(Bundle.getMessage("AudioSourceFadeIn"));      // NOI18N
3995                    _actionBox.addItem(Bundle.getMessage("AudioSourceFadeOut"));     // NOI18N
3996                    _actionBox.addItem(Bundle.getMessage("AudioResetPosition"));     // NOI18N
3997                }
3998
3999                makeDetailGrid(audioGrid);
4000                break;
4001
4002            case SCRIPT:
4003                String scriptGrid = "TypeAction";  // NOI18N
4004
4005                for (Conditional.Action action : Conditional.Action.getScriptItems()) {
4006                    _actionTypeBox.addItem(action);
4007                }
4008
4009                if (actionType == Conditional.Action.RUN_SCRIPT) {
4010                    scriptGrid = "FileAction";  // NOI18N
4011                    _shortActionLabel.setText(Bundle.getMessage("LabelSelectFile"));  // NOI18N
4012                    _actionSetButton.setToolTipText(Bundle.getMessage("SetHintScript"));  // NOI18N
4013                } else if (actionType == Conditional.Action.JYTHON_COMMAND) {
4014                    scriptGrid = "TypeShortAction";  // NOI18N
4015                    _shortActionLabel.setText(Bundle.getMessage("LabelScriptCommand"));  // NOI18N
4016                    _shortActionLabel.setToolTipText(Bundle.getMessage("SetHintJythonCmd"));  // NOI18N
4017                }
4018
4019                makeDetailGrid(scriptGrid);
4020                break;
4021
4022            case OTHER:
4023                String otherGrid = "TypeAction";  // NOI18N
4024
4025                for (Conditional.Action action : Conditional.Action.getOtherItems()) {
4026                    _actionTypeBox.addItem(action);
4027                }
4028
4029                if (actionType == Conditional.Action.TRIGGER_ROUTE) {
4030                    otherGrid = "NameTypeActionFinal";  // NOI18N
4031                    _actionNameLabel.setToolTipText(Bundle.getMessage("NameHintRoute"));  // NOI18N
4032                }
4033
4034                setActionNameBox(itemType);
4035                makeDetailGrid(otherGrid);
4036                break;
4037
4038            default:
4039                break;
4040        }
4041        _actionTypeListener.setItemType(itemType);
4042        _actionTypeBox.addActionListener(_actionTypeListener);
4043    }
4044
4045    /**
4046     * Update the name combo box selection based on the current contents of the
4047     * name field.
4048     *
4049     * @since 4.7.3
4050     * @param itemType The item type, such as sensor or turnout.
4051     */
4052    void setActionNameBox(Conditional.ItemType itemType) {
4053        if (_selectionMode != SelectionMode.USECOMBO) {
4054            return;
4055        }
4056        _comboNameBox = createNameBox(itemType);
4057        if (_comboNameBox == null) {
4058            return;
4059        }
4060        // Select the current entry
4061        _comboNameBox.setSelectedItemByName(_curAction.getDeviceName());
4062        _comboNameBox.addActionListener(new NameBoxListener(_actionNameField));
4063        _comboNameBox.addFocusListener(detailFocusEvent);
4064    }
4065
4066    // ------------ Action detail methods ------------
4067
4068    /**
4069     * Respond to Cancel action button and window closer of the Edit Action
4070     * window.
4071     * <p>
4072     * Also does cleanup of Update and Delete buttons.
4073     */
4074    void cancelEditAction() {
4075        if (_newActionItem) {
4076            _newActionItem = false;
4077            deletePressed();
4078        }
4079        cleanUpAction();
4080    }
4081
4082    /**
4083     * Respond to Update button.
4084     */
4085    void updateAction() {
4086        if (!validateAction()) {
4087            return;
4088        }
4089        _newActionItem = false;
4090        updateActionList();
4091
4092        // Update the Action node
4093        _curNode.setText(_curAction.description(_triggerMode));
4094        _cdlModel.nodeChanged(_curNode);
4095        cleanUpAction();
4096    }
4097
4098    /**
4099     * Clean up Update and Delete Action buttons.
4100     */
4101    void cleanUpAction() {
4102        setMoveButtons();
4103    }
4104
4105    /*.*
4106     * Convert user setting in Conditional Action configuration pane to integer
4107     * for processing.
4108     *
4109     * @param itemType            value for current item type
4110     * @param actionTypeSelection index of selected item in configuration
4111     *                            comboBox
4112     * @return integer representing the selected action
4113     */
4114/*    static Conditional.Action getActionTypeFromBox(Conditional.ItemType itemType, int actionTypeSelection) {
4115//        if (itemType < 0 || actionTypeSelection < 0) {
4116//            return Conditional.Action.NONE;
4117//        }
4118        if (actionTypeSelection < 0) {
4119            return Conditional.Action.NONE;
4120        }
4121        switch (itemType) {
4122            case SENSOR:
4123                return Conditional.Action.getSensorItems().get(actionTypeSelection);
4124            case TURNOUT:
4125                return Conditional.Action.getTurnoutItems().get(actionTypeSelection);
4126            case LIGHT:
4127                return Conditional.Action.getLightItems().get(actionTypeSelection);
4128            case SIGNALHEAD:
4129                return Conditional.Action.getSignalHeadItems().get(actionTypeSelection);
4130            case SIGNALMAST:
4131                return Conditional.Action.getSignalMastItems().get(actionTypeSelection);
4132            case MEMORY:
4133                return Conditional.Action.getMemoryItems().get(actionTypeSelection);
4134            case LOGIX:
4135                return Conditional.Action.getLogixItems().get(actionTypeSelection);
4136            case WARRANT:
4137                return Conditional.Action.getWarrantItems().get(actionTypeSelection);
4138            case OBLOCK:
4139                return Conditional.Action.getOBlockItems().get(actionTypeSelection);
4140            case CLOCK:
4141                return Conditional.Action.getClockItems().get(actionTypeSelection);
4142            case AUDIO:
4143                return Conditional.Action.getAudioItems().get(actionTypeSelection);
4144            case SCRIPT:
4145                return Conditional.Action.getScriptItems().get(actionTypeSelection);
4146            case OTHER:
4147                return Conditional.Action.getOtherItems().get(actionTypeSelection);
4148            case ENTRYEXIT:
4149                return Conditional.Action.getEntryExitItems().get(actionTypeSelection);
4150            default:
4151                // fall through
4152                break;
4153        }
4154        return Conditional.Action.NONE;
4155    }
4156*/
4157    JFileChooser sndFileChooser = null;
4158    ScriptFileChooser scriptFileChooser = null;
4159    JFileChooser defaultFileChooser = null;
4160
4161    /**
4162     * Respond to the [...] button in the Edit Action window action section.
4163     * <p>
4164     * Ask user to select an audio or python script file on disk.
4165     *
4166     * @param e the event heard
4167     */
4168    void setFileLocation(ActionEvent e) {
4169        ConditionalAction action = _actionList.get(_curNodeRow);
4170        JFileChooser currentChooser;
4171        Conditional.Action actionType = action.getType();
4172        if (actionType == Conditional.Action.PLAY_SOUND) {
4173            if (sndFileChooser == null) {
4174                sndFileChooser = new jmri.util.swing.JmriJFileChooser(System.getProperty("user.dir") // NOI18N
4175                        + java.io.File.separator + "resources" // NOI18N
4176                        + java.io.File.separator + "sounds");  // NOI18N
4177                sndFileChooser.setFileFilter(new FileNameExtensionFilter("wav sound files", "wav")); // NOI18N
4178            }
4179            currentChooser = sndFileChooser;
4180        } else if (actionType == Conditional.Action.RUN_SCRIPT) {
4181            if (scriptFileChooser == null) {
4182                scriptFileChooser = new ScriptFileChooser(FileUtil.getScriptsPath());
4183            }
4184            currentChooser = scriptFileChooser;
4185        } else {
4186            log.warn("Unexpected actionType[{}] = {}", actionType.name(), actionType);  // NOI18N
4187            if (defaultFileChooser == null) {
4188                defaultFileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath());
4189                defaultFileChooser.setFileFilter(new jmri.util.NoArchiveFileFilter());
4190            }
4191            currentChooser = defaultFileChooser;
4192        }
4193
4194        currentChooser.rescanCurrentDirectory();
4195        int retVal = currentChooser.showOpenDialog(null);
4196        // handle selection or cancel
4197        if (retVal == JFileChooser.APPROVE_OPTION) {
4198            // set selected file location in data string
4199            try {
4200                _longActionString.setText(FileUtil.getPortableFilename(currentChooser.getSelectedFile().getCanonicalPath()));
4201            } catch (java.io.IOException ex) {
4202                if (log.isDebugEnabled()) {
4203                    log.error("exception setting file location: ", ex);  // NOI18N
4204                }
4205                _longActionString.setText("");
4206            }
4207        }
4208    }
4209
4210    // ------------ Action update processes ------------
4211
4212    /**
4213     * Validate Action data from Edit Action Window, and transfer it to current
4214     * action object as appropriate.
4215     * <p>
4216     * Messages are sent to the user for any errors found. This routine returns
4217     * false immediately after finding an error, even if there might be more
4218     * errors.
4219     *
4220     * @return true if all data checks out OK, otherwise false.
4221     */
4222    boolean validateAction() {
4223        Conditional.ItemType itemType = _actionItemBox.getItemAt(_actionItemBox.getSelectedIndex());
4224        Conditional.Action actionType = Conditional.Action.NONE;
4225        Conditional.Action selection = _actionTypeBox.getItemAt(_actionTypeBox.getSelectedIndex());
4226        if (selection == Conditional.Action.NONE) {
4227           JmriJOptionPane.showMessageDialog(
4228                    _editLogixFrame, Bundle.getMessage("makeSelection"),
4229                    Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE);
4230            return false;
4231        }
4232        String name = _actionNameField.getText().trim();
4233        String actionString = _shortActionString.getText().trim();
4234        _curAction.setActionString("");
4235        _curAction.setActionData(-1);
4236        boolean referenceByMemory = false;
4237        if (name.length() > 0 && name.charAt(0) == '@') {
4238            String memName = name.substring(1);
4239            if (!confirmIndirectMemory(memName)) {
4240                return false;
4241            }
4242            memName = validateMemoryReference(memName);
4243            if (memName == null) {
4244                return false;
4245            }
4246            referenceByMemory = true;
4247        }
4248        if (!checkIsVariable(name, itemType) ) {
4249            return false;
4250        }
4251        switch (itemType) {
4252            case SENSOR:
4253                if (!referenceByMemory) {
4254                    name = validateSensorReference(name);
4255                    if (name == null) {
4256                        return false;
4257                    }
4258                }
4259                actionType = selection;
4260                if ((actionType == Conditional.Action.RESET_DELAYED_SENSOR)
4261                        || (actionType == Conditional.Action.DELAYED_SENSOR)) {
4262                    if (!validateTimeReference(actionType, actionString)) {
4263                        return (false);
4264                    }
4265                    _curAction.setActionString(actionString);
4266                }
4267                if ((actionType == Conditional.Action.SET_SENSOR)
4268                        || (actionType == Conditional.Action.RESET_DELAYED_SENSOR)
4269                        || (actionType == Conditional.Action.DELAYED_SENSOR)) {
4270                    if (_actionBox.getSelectedIndex() == 0) {
4271                        _curAction.setActionData(Sensor.ACTIVE);
4272                    } else if (_actionBox.getSelectedIndex() == 1) {
4273                        _curAction.setActionData(Sensor.INACTIVE);
4274                    } else {
4275                        _curAction.setActionData(Route.TOGGLE);
4276                    }
4277                }
4278                _actionNameField.setText(name);
4279                _curAction.setDeviceName(name);
4280                break;
4281            case TURNOUT:
4282                if (!referenceByMemory) {
4283                    name = validateTurnoutReference(name);
4284                    if (name == null) {
4285                        return false;
4286                    }
4287                }
4288                actionType = selection;
4289                if ((actionType == Conditional.Action.RESET_DELAYED_TURNOUT)
4290                        || (actionType == Conditional.Action.DELAYED_TURNOUT)) {
4291                    if (!validateTimeReference(actionType, actionString)) {
4292                        return (false);
4293                    }
4294                    _curAction.setActionString(actionString);
4295                }
4296                if ((actionType == Conditional.Action.SET_TURNOUT)
4297                        || (actionType == Conditional.Action.RESET_DELAYED_TURNOUT)
4298                        || (actionType == Conditional.Action.DELAYED_TURNOUT)) {
4299                    if (_actionBox.getSelectedIndex() == 0) {
4300                        _curAction.setActionData(Turnout.CLOSED);
4301                    } else if (_actionBox.getSelectedIndex() == 1) {
4302                        _curAction.setActionData(Turnout.THROWN);
4303                    } else {
4304                        _curAction.setActionData(Route.TOGGLE);
4305                    }
4306                } else if (actionType == Conditional.Action.LOCK_TURNOUT) {
4307                    if (_actionBox.getSelectedIndex() == 0) {
4308                        _curAction.setActionData(Turnout.UNLOCKED);
4309                    } else if (_actionBox.getSelectedIndex() == 1) {
4310                        _curAction.setActionData(Turnout.LOCKED);
4311                    } else {
4312                        _curAction.setActionData(Route.TOGGLE);
4313                    }
4314                }
4315                _actionNameField.setText(name);
4316                _curAction.setDeviceName(name);
4317                break;
4318            case LIGHT:
4319                if (!referenceByMemory) {
4320                    name = validateLightReference(name);
4321                    if (name == null) {
4322                        return false;
4323                    }
4324                }
4325                actionType = selection;
4326                if (actionType == Conditional.Action.SET_LIGHT_INTENSITY) {
4327                    Light lgtx = getLight(name);
4328                    // check if light user name was entered
4329                    if (lgtx == null) {
4330                        return false;
4331                    }
4332                    if (!(lgtx instanceof VariableLight)) {
4333                       JmriJOptionPane.showMessageDialog(_editLogixFrame,
4334                                Bundle.getMessage("Error45", name), // NOI18N
4335                                Bundle.getMessage("ErrorTitle"),
4336                                JmriJOptionPane.ERROR_MESSAGE);  // NOI18N
4337                        return (false);
4338                    }
4339                    if (!validateIntensityReference(actionType, actionString)) {
4340                        return (false);
4341                    }
4342                    _curAction.setActionString(actionString);
4343                } else if (actionType == Conditional.Action.SET_LIGHT_TRANSITION_TIME) {
4344                    Light lgtx = getLight(name);
4345                    // check if light user name was entered
4346                    if (lgtx == null) {
4347                        return false;
4348                    }
4349                    if ( !(lgtx instanceof VariableLight)
4350                            || !((VariableLight)lgtx).isTransitionAvailable()) {
4351                       JmriJOptionPane.showMessageDialog(_editLogixFrame,
4352                                Bundle.getMessage("Error40", name), // NOI18N
4353                                Bundle.getMessage("ErrorTitle"),
4354                                JmriJOptionPane.ERROR_MESSAGE);  // NOI18N
4355                        return (false);
4356                    }
4357                    if (!validateTimeReference(actionType, actionString)) {
4358                        return (false);
4359                    }
4360                    _curAction.setActionString(actionString);
4361                } else if (actionType == Conditional.Action.SET_LIGHT) {
4362                    if (_actionBox.getSelectedIndex() == 0) {
4363                        _curAction.setActionData(Light.ON);
4364                    } else if (_actionBox.getSelectedIndex() == 1) {
4365                        _curAction.setActionData(Light.OFF);
4366                    } else {
4367                        _curAction.setActionData(Route.TOGGLE);
4368                    }
4369                }
4370                _actionNameField.setText(name);
4371                _curAction.setDeviceName(name);
4372                break;
4373            case SIGNALHEAD:
4374                if (!referenceByMemory) {
4375                    name = validateSignalHeadReference(name);
4376                    if (name == null) {
4377                        return false;
4378                    }
4379                }
4380                actionType = selection;
4381                if (actionType == Conditional.Action.SET_SIGNAL_APPEARANCE) {
4382                    String appStr = (String) _actionBox.getSelectedItem();
4383                    _curAction.setActionData(DefaultConditionalAction.stringToActionData(appStr));
4384                    _curAction.setActionString(appStr);
4385                }
4386                _actionNameField.setText(name);
4387                _curAction.setDeviceName(name);
4388                break;
4389            case SIGNALMAST:
4390                if (!referenceByMemory) {
4391                    name = validateSignalMastReference(name);
4392                    if (name == null) {
4393                        return false;
4394                    }
4395                }
4396                actionType = selection;
4397                if (actionType == Conditional.Action.SET_SIGNALMAST_ASPECT) {
4398                    _curAction.setActionString((String) _actionBox.getSelectedItem());
4399                }
4400                _actionNameField.setText(name);
4401                _curAction.setDeviceName(name);
4402                break;
4403            case MEMORY:
4404                if (referenceByMemory) {
4405                   JmriJOptionPane.showMessageDialog(_editLogixFrame, Bundle.getMessage("Warn6"), Bundle.getMessage("WarningTitle"), // NOI18N
4406                            JmriJOptionPane.WARNING_MESSAGE);
4407                    return false;
4408                }
4409                name = validateMemoryReference(name);
4410                if (name == null) {
4411                    return false;
4412                }
4413                actionType = selection;
4414                if (actionType == Conditional.Action.COPY_MEMORY) {
4415                    actionString = validateMemoryReference(actionString);
4416                    if (actionString == null) {
4417                        return false;
4418                    }
4419                }
4420                _actionNameField.setText(name);
4421                _curAction.setDeviceName(name);
4422                _curAction.setActionString(actionString);
4423                break;
4424            case LOGIX:
4425                if (!referenceByMemory) {
4426                    name = validateLogixReference(name);
4427                    if (name == null) {
4428                        return false;
4429                    }
4430                }
4431                actionType = selection;
4432                _actionNameField.setText(name);
4433                _curAction.setDeviceName(name);
4434                break;
4435            case WARRANT:
4436                if (!referenceByMemory) {
4437                    name = validateWarrantReference(name);
4438                    if (name == null) {
4439                        return false;
4440                    }
4441                }
4442                actionType = selection;
4443                _actionNameField.setText(name);
4444                _curAction.setDeviceName(name);
4445                if (actionType == Conditional.Action.CONTROL_TRAIN) {
4446                    switch (_actionBox.getSelectedIndex()) {
4447                        case 0:
4448                            _curAction.setActionData(Warrant.HALT);
4449                            break;
4450                        case 1:
4451                            _curAction.setActionData(Warrant.RESUME);
4452                            break;
4453                        case 2:
4454                            _curAction.setActionData(Warrant.RETRY_FWD);
4455                            break;
4456                        case 3:
4457                            _curAction.setActionData(Warrant.SPEED_UP);
4458                            break;
4459                        case 4:
4460                            _curAction.setActionData(Warrant.STOP);
4461                            break;
4462                        case 5:
4463                            _curAction.setActionData(Warrant.ESTOP);
4464                            break;
4465                        case 6:
4466                            _curAction.setActionData(Warrant.ABORT);
4467                            break;
4468                        default:
4469                            log.warn("Unexpected _actionBox.getSelectedIndex() of {}", _actionBox.getSelectedIndex());  // NOI18N
4470                            break;
4471                    }
4472                } else if (actionType == Conditional.Action.SET_TRAIN_ID
4473                        || actionType == Conditional.Action.SET_TRAIN_NAME
4474                        || actionType == Conditional.Action.GET_TRAIN_LOCATION) {
4475                    _curAction.setActionString(actionString);
4476                }
4477                break;
4478            case OBLOCK:
4479                if (!referenceByMemory) {
4480                    name = validateOBlockReference(name);
4481                    if (name == null) {
4482                        return false;
4483                    }
4484                }
4485                actionType = selection;
4486                _actionNameField.setText(name);
4487                _curAction.setDeviceName(name);
4488                if (actionType == Conditional.Action.SET_BLOCK_VALUE
4489                        || actionType == Conditional.Action.SET_TRAIN_NAME
4490                        || actionType == Conditional.Action.GET_TRAIN_LOCATION) {
4491                    _curAction.setActionString(actionString);
4492                }
4493                break;
4494            case ENTRYEXIT:
4495                if (!referenceByMemory) {
4496                    name = validateEntryExitReference(name);
4497                    if (name == null) {
4498                        return false;
4499                    }
4500                }
4501                actionType = selection;
4502                _actionNameField.setText(name);
4503                _curAction.setDeviceName(name);
4504                break;
4505            case CLOCK:
4506                actionType = selection;
4507                if (actionType == Conditional.Action.SET_FAST_CLOCK_TIME) {
4508                    int time = parseTime(_shortActionString.getText().trim());
4509                    if (time < 0) {
4510                        return (false);
4511                    }
4512                    _curAction.setActionData(time);
4513                }
4514                break;
4515            case AUDIO:
4516                actionType = selection;
4517                if (actionType == Conditional.Action.PLAY_SOUND) {
4518                    _curAction.setActionString(_longActionString.getText().trim());
4519                } else if (actionType == Conditional.Action.CONTROL_AUDIO) {
4520                    if (!referenceByMemory) {
4521                        name = validateAudioReference(name);
4522                        if (name == null) {
4523                            return false;
4524                        }
4525                    }
4526                    _actionNameField.setText(name);
4527                    _curAction.setDeviceName(name);
4528                    switch (_actionBox.getSelectedIndex()) {
4529                        case 0:
4530                            _curAction.setActionData(Audio.CMD_PLAY);
4531                            break;
4532                        case 1:
4533                            _curAction.setActionData(Audio.CMD_STOP);
4534                            break;
4535                        case 2:
4536                            _curAction.setActionData(Audio.CMD_PLAY_TOGGLE);
4537                            break;
4538                        case 3:
4539                            _curAction.setActionData(Audio.CMD_PAUSE);
4540                            break;
4541                        case 4:
4542                            _curAction.setActionData(Audio.CMD_RESUME);
4543                            break;
4544                        case 5:
4545                            _curAction.setActionData(Audio.CMD_PAUSE_TOGGLE);
4546                            break;
4547                        case 6:
4548                            _curAction.setActionData(Audio.CMD_REWIND);
4549                            break;
4550                        case 7:
4551                            _curAction.setActionData(Audio.CMD_FADE_IN);
4552                            break;
4553                        case 8:
4554                            _curAction.setActionData(Audio.CMD_FADE_OUT);
4555                            break;
4556                        case 9:
4557                            _curAction.setActionData(Audio.CMD_RESET_POSITION);
4558                            break;
4559                        default:
4560                            log.warn("Unexpected _actionBox.getSelectedIndex() of {}", _actionBox.getSelectedIndex());  // NOI18N
4561                            break;
4562                    }
4563                }
4564                break;
4565            case SCRIPT:
4566                actionType = selection;
4567                if (actionType == Conditional.Action.RUN_SCRIPT) {
4568                    _curAction.setActionString(_longActionString.getText().trim());
4569                } else if (actionType == Conditional.Action.JYTHON_COMMAND) {
4570                    _curAction.setActionString(_shortActionString.getText().trim());
4571                }
4572                break;
4573            case OTHER:
4574                actionType = selection;
4575                if (actionType == Conditional.Action.TRIGGER_ROUTE) {
4576                    if (!referenceByMemory) {
4577                        name = validateRouteReference(name);
4578                        if (name == null) {
4579                            return false;
4580                        }
4581                    }
4582                    _actionNameField.setText(name);
4583                    _curAction.setDeviceName(name);
4584                }
4585                break;
4586            default:
4587                break;
4588        }
4589        _curAction.setType(actionType);
4590        if (actionType != Conditional.Action.NONE) {
4591            _curAction.setOption(_actionOptionBox.getSelectedIndex() + 1);
4592        } else {
4593            _curAction.setOption(0);
4594        }
4595        return (true);
4596    }
4597
4598    /**
4599     * Update the conditional action list and refresh the local copy.
4600     * The parent Logix is de-activated and re-activated.  This ensures
4601     * that listeners are properly handled, specifically the delayed sensor
4602     * and turnout options.
4603     * @since 4.11.2
4604     */
4605    void updateActionList() {
4606        _curLogix.deActivateLogix();
4607        _curConditional.setAction(_actionList);
4608        _actionList = _curConditional.getCopyOfActions();
4609        _curLogix.activateLogix();
4610    }
4611
4612    /**
4613     * Check that a state variable is not also used as an action
4614     *
4615     * @param name of the state variable
4616     * @param itemType item type of the state variable
4617     * @return true if variable is not an action of if the user OK's
4618     * its use as an action also.
4619     */
4620    boolean checkIsAction(String name, Conditional.ItemType itemType) {
4621        String actionName = null;
4622        for (ConditionalAction action : _actionList) {
4623            Conditional.ItemType actionType = action.getType().getItemType();
4624            if (itemType == actionType) {
4625                if (name.equals(action.getDeviceName())) {
4626                    actionName = action.getDeviceName();
4627                } else {
4628                    NamedBean bean  = action.getBean();
4629                    if (bean != null &&
4630                        (name.equals(bean.getSystemName()) ||
4631                                name.equals(bean.getUserName()))) {
4632                        actionName = action.getDeviceName();
4633                   }
4634                }
4635            }
4636            if (actionName != null) {
4637                return confirmActionAsVariable(actionName, name);
4638            }
4639        }
4640        return true;
4641    }
4642
4643    /**
4644     * Check that an action is not also used as a state variable
4645     *
4646     * @param name of the action
4647     * @param itemType item type of the action
4648     * @return true if action is not a state variable of if the user OK's
4649     * its use as such.
4650     */
4651    boolean checkIsVariable(String name, Conditional.ItemType itemType) {
4652        String varName = null;
4653        for (ConditionalVariable var : _variableList) {
4654            Conditional.ItemType varType = var.getType().getItemType();
4655            if (itemType == varType) {
4656                if (name.equals(var.getName())) {
4657                    varName = var.getName();
4658                } else {
4659                    NamedBean bean  = var.getBean();
4660                    if (bean != null &&
4661                        (name.equals(bean.getSystemName()) ||
4662                                name.equals(bean.getUserName()))) {
4663                        varName = var.getName();
4664                   }
4665                }
4666            }
4667            if (varName != null) {
4668                return confirmActionAsVariable(name, varName);
4669            }
4670        }
4671        return true;
4672    }
4673
4674    // ------------ Action detail listeners ------------
4675
4676    /**
4677     * Listener for _actionTypeBox.
4678     */
4679    class ActionTypeListener implements ActionListener {
4680
4681        Conditional.ItemType _itemType;
4682
4683        @Override
4684        public void actionPerformed(ActionEvent e) {
4685            Conditional.ItemType select1 = _actionItemBox.getItemAt(_actionItemBox.getSelectedIndex());
4686            Conditional.Action select2 = _actionTypeBox.getItemAt(_actionTypeBox.getSelectedIndex());
4687            if (log.isDebugEnabled()) {
4688                log.debug("ActionTypeListener: itemType = {}, local itemType = {}, actionType = {}",  // NOI18N
4689                        select1, _itemType, select2);
4690            }
4691            if (select1 != _itemType) {
4692                log.error("ActionTypeListener actionItem selection ({}) != expected actionItem ({})",  // NOI18N
4693                        select1, _itemType);
4694            }
4695            if (_curAction != null) {
4696                if (select1 != Conditional.ItemType.NONE && _itemType == select1) {
4697                    _curAction.setType(select2);
4698                    if (select1 == _itemType) {
4699                        String text = _actionNameField.getText();
4700                        if (text != null && text.length() > 0) {
4701                            _curAction.setDeviceName(text);
4702                        }
4703                    }
4704                    actionItemChanged(_itemType);
4705                    initializeActionVariables();
4706                }
4707            }
4708        }
4709
4710        public void setItemType(Conditional.ItemType type) {
4711            _itemType = type;
4712        }
4713    }
4714
4715    ActionTypeListener _actionTypeListener = new ActionTypeListener();
4716
4717    transient ActionListener actionSignalHeadNameListener = new ActionListener() {
4718        @Override
4719        public void actionPerformed(ActionEvent e) {
4720            // fired when signal head name changes, but only
4721            // while in signal head mode
4722            log.debug("actionSignalHeadNameListener fires; _actionNameField : {}", _actionNameField.getText().trim());  // NOI18N
4723            loadJComboBoxWithHeadAppearances(_actionBox, _actionNameField.getText().trim());
4724        }
4725    };
4726
4727    transient ActionListener actionSignalMastNameListener = new ActionListener() {
4728        @Override
4729        public void actionPerformed(ActionEvent e) {
4730            // fired when signal mast name changes, but only
4731            // while in signal mast mode
4732            log.debug("actionSignalMastNameListener fires; _actionNameField : {}", _actionNameField.getText().trim());  // NOI18N
4733            loadJComboBoxWithMastAspects(_actionBox, _actionNameField.getText().trim());
4734        }
4735    };
4736
4737    /**
4738     * Conditional Tree Node Definition.
4739     */
4740    static class ConditionalTreeNode extends DefaultMutableTreeNode {
4741
4742        private String cdlText;
4743        private String cdlType;
4744        private String cdlName;
4745        private int cdlRow;
4746
4747        public ConditionalTreeNode(String nameText, String type, String sysName, int row) {
4748            this.cdlText = nameText;
4749            this.cdlType = type;
4750            this.cdlName = sysName;
4751            this.cdlRow = row;
4752        }
4753
4754        public String getType() {
4755            return cdlType;
4756        }
4757
4758        public String getName() {
4759            return cdlName;
4760        }
4761
4762        public void setName(String newName) {
4763            cdlName = newName;
4764        }
4765
4766        public int getRow() {
4767            return cdlRow;
4768        }
4769
4770        public void setRow(int newRow) {
4771            cdlRow = newRow;
4772        }
4773
4774        public String getText() {
4775            return cdlText;
4776        }
4777
4778        public void setText(String newText) {
4779            cdlText = newText;
4780        }
4781
4782        @Override
4783        public String toString() {
4784            return cdlText;
4785        }
4786    }
4787
4788    @Override
4789    protected String getClassName() {
4790        return ConditionalTreeEdit.class.getName();
4791    }
4792
4793    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConditionalTreeEdit.class);
4794
4795}