001package jmri.jmrit.beantable;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.awt.event.ItemEvent;
007import java.awt.event.ItemListener;
008import java.awt.event.KeyEvent;
009import java.io.File;
010import java.io.IOException;
011import java.util.ArrayList;
012import java.util.HashMap;
013import java.util.List;
014import java.util.SortedSet;
015import java.util.TreeSet;
016
017import javax.annotation.Nonnull;
018import javax.swing.BorderFactory;
019import javax.swing.Box;
020import javax.swing.BoxLayout;
021import javax.swing.ButtonGroup;
022import javax.swing.DefaultCellEditor;
023import javax.swing.JButton;
024import javax.swing.JCheckBox;
025import javax.swing.JComboBox;
026import javax.swing.JComponent;
027import javax.swing.JDialog;
028import javax.swing.JFileChooser;
029import javax.swing.JLabel;
030import javax.swing.JMenu;
031import javax.swing.JMenuItem;
032import javax.swing.JPanel;
033import javax.swing.JRadioButtonMenuItem;
034import javax.swing.JScrollPane;
035import javax.swing.JTable;
036import javax.swing.JTextArea;
037import javax.swing.JTextField;
038import javax.swing.table.TableColumn;
039
040import jmri.*;
041import jmri.NamedBean.DisplayOptions;
042import jmri.jmrit.conditional.ConditionalEditBase;
043import jmri.jmrit.conditional.ConditionalListEdit;
044import jmri.jmrit.conditional.ConditionalTreeEdit;
045import jmri.jmrit.conditional.ConditionalListCopy;
046import jmri.jmrit.logixng.tools.ImportLogix;
047import jmri.jmrit.sensorgroup.SensorGroupFrame;
048import jmri.util.FileUtil;
049import jmri.util.JmriJFrame;
050import jmri.util.swing.JmriJOptionPane;
051
052import org.slf4j.Logger;
053import org.slf4j.LoggerFactory;
054
055/**
056 * Swing action to create and register a Logix Table.
057 * <p>
058 * Also contains the panes to create, edit, and delete a Logix. Conditional
059 * editing has been moved to ConditionalListView or CondtionalTreeView.
060 * <p>
061 * Most of the text used in this GUI is in BeanTableBundle.properties, accessed
062 * via Bundle.getMessage().
063 * 201803 Moved all keys from LogixTableBundle.properties to
064 * BeanTableBundle.properties to simplify i18n.
065 * <p>
066 * Conditionals now have two policies to trigger execution of their action lists:
067 * <ol>
068 *     <li>the previous policy - Trigger on change of state only
069 *     <li>the new default - Trigger on any enabled state calculation
070 * </ol>
071 * Jan 15, 2011 - Pete Cressman
072 * <p>
073 * Two additional action and variable name selection methods have been added:
074 * <ol>
075 *     <li>Single Pick List
076 *     <li>Combo Box Selection
077 * </ol>
078 * The traditional tabbed Pick List with text entry is the default method.
079 * The Options menu has been expanded to list the 3 methods.
080 * Mar 27, 2017 - Dave Sand
081 * <p>
082 * Add a Browse Option to the Logix Select Menu This will display a window that
083 * creates a formatted list of the contents of the selected Logix with each
084 * Conditional, Variable and Action. The code is courtesy of Chuck Catania and
085 * is used with his permission. Apr 2, 2017 - Dave Sand
086 *
087 * @author Dave Duchamp Copyright (C) 2007
088 * @author Pete Cressman Copyright (C) 2009, 2010, 2020
089 * @author Matthew Harris copyright (c) 2009
090 * @author Dave Sand copyright (c) 2017
091 */
092public class LogixTableAction extends AbstractTableAction<Logix> {
093
094    /**
095     * Create a LogixManager instance.
096     *
097     * @param s the Action title, not the title of the resulting frame. Perhaps
098     *          this should be changed?
099     */
100    public LogixTableAction(String s) {
101        super(s);
102        // set up managers - no need to use InstanceManager since both managers are
103        // Default only (internal). We use InstanceManager to get managers for
104        // compatibility with other facilities.
105        _logixManager = InstanceManager.getNullableDefault(jmri.LogixManager.class);
106        _conditionalManager = InstanceManager.getNullableDefault(jmri.ConditionalManager.class);
107        // disable ourself if there is no Logix manager or no Conditional manager available
108        if ((_logixManager == null) || (_conditionalManager == null)) {
109            setEnabled(false);
110        }
111    }
112
113    /**
114     * Create a LogixManager instance with default title.
115     */
116    public LogixTableAction() {
117        this(Bundle.getMessage("TitleLogixTable"));
118    }
119
120    // ------------ Methods for Logix Table Window ------------
121
122    /**
123     * Create the JTable DataModel, along with the changes (overrides of
124     * BeanTableDataModel) for the specific case of a Logix table.
125     */
126    @Override
127    protected void createModel() {
128        m = new BeanTableDataModel<Logix>() {
129            // overlay the state column with the edit column
130            static public final int ENABLECOL = VALUECOL;
131            static public final int EDITCOL = DELETECOL;
132            protected String enabledString = Bundle.getMessage("ColumnHeadEnabled");  // NOI18N
133
134            @Override
135            public String getColumnName(int col) {
136                if (col == EDITCOL) {
137                    return "";  // no heading on "Edit"
138                }
139                if (col == ENABLECOL) {
140                    return enabledString;
141                }
142                return super.getColumnName(col);
143            }
144
145            @Override
146            public Class<?> getColumnClass(int col) {
147                if (col == EDITCOL) {
148                    return String.class;
149                }
150                if (col == ENABLECOL) {
151                    return Boolean.class;
152                }
153                return super.getColumnClass(col);
154            }
155
156            @Override
157            public int getPreferredWidth(int col) {
158                // override default value for SystemName and UserName columns
159                if (col == SYSNAMECOL) {
160                    return new JTextField(12).getPreferredSize().width;
161                }
162                if (col == USERNAMECOL) {
163                    return new JTextField(17).getPreferredSize().width;
164                }
165                if (col == EDITCOL) {
166                    return new JTextField(12).getPreferredSize().width;
167                }
168                if (col == ENABLECOL) {
169                    return new JTextField(5).getPreferredSize().width;
170                }
171                return super.getPreferredWidth(col);
172            }
173
174            @Override
175            public boolean isCellEditable(int row, int col) {
176                if (col == EDITCOL) {
177                    return true;
178                }
179                if (col == ENABLECOL) {
180                    return true;
181                }
182                return super.isCellEditable(row, col);
183            }
184
185            @Override
186            public Object getValueAt(int row, int col) {
187                if (col == EDITCOL) {
188                    return Bundle.getMessage("ButtonSelect");  // NOI18N
189                } else if (col == ENABLECOL) {
190                    Logix logix = (Logix) getValueAt(row, SYSNAMECOL);
191                    if (logix == null) {
192                        return null;
193                    }
194                    return Boolean.valueOf(logix.getEnabled());
195                } else {
196                    return super.getValueAt(row, col);
197                }
198            }
199
200            @Override
201            public void setValueAt(Object value, int row, int col) {
202                if (col == EDITCOL) {
203                    // set up to edit
204                    String sName = ((Logix) getValueAt(row, SYSNAMECOL)).getSystemName();
205                    if (Bundle.getMessage("ButtonEdit").equals(value)) {  // NOI18N
206                        editPressed(sName);
207
208                    } else if (Bundle.getMessage("BrowserButton").equals(value)) {  // NOI18N
209                        conditionalRowNumber = row;
210                        browserPressed(sName);
211
212                    } else if (Bundle.getMessage("ButtonCopy").equals(value)) {  // NOI18N
213                        copyPressed(sName);
214
215                    } else if (Bundle.getMessage("ButtonDelete").equals(value)) {  // NOI18N
216                        deletePressed(sName);
217                    } else if (Bundle.getMessage("ButtonExportLogixToLogixNG").equals(value)) {  // NOI18N
218                        exportToLogixNGPressed(sName);
219                    }
220                } else if (col == ENABLECOL) {
221                    // alternate
222                    Logix x = (Logix) getValueAt(row, SYSNAMECOL);
223                    boolean v = x.getEnabled();
224                    x.setEnabled(!v);
225                } else {
226                    super.setValueAt(value, row, col);
227                }
228            }
229
230            /**
231             * Delete the bean after all the checking has been done.
232             * <p>
233             * Deactivates the Logix and remove its conditionals.
234             *
235             * @param bean of the Logix to delete
236             */
237            @Override
238            protected void doDelete(Logix bean) {
239                bean.deActivateLogix();
240                // delete the Logix and all its Conditionals
241                _logixManager.deleteLogix(bean);
242            }
243
244            @Override
245            protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
246                if (e.getPropertyName().equals(enabledString)) {
247                    return true;
248                }
249                return super.matchPropertyName(e);
250            }
251
252            @Override
253            public Manager<Logix> getManager() {
254                return InstanceManager.getDefault(jmri.LogixManager.class);
255            }
256
257            @Override
258            public Logix getBySystemName(@Nonnull String name) {
259                return InstanceManager.getDefault(jmri.LogixManager.class).getBySystemName(
260                        name);
261            }
262
263            @Override
264            public Logix getByUserName(@Nonnull String name) {
265                return InstanceManager.getDefault(jmri.LogixManager.class).getByUserName(
266                        name);
267            }
268
269            @Override
270            protected String getMasterClassName() {
271                return getClassName();
272            }
273
274            @Override
275            public void configureTable(JTable table) {
276                table.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer());
277                table.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
278                table.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
279                super.configureTable(table);
280            }
281
282            /**
283             * Replace delete button with comboBox to edit/delete/copy/select
284             * Logix.
285             *
286             * @param table name of the Logix JTable holding the column
287             */
288            @Override
289            protected void configDeleteColumn(JTable table) {
290                JComboBox<String> editCombo = new JComboBox<String>();
291                editCombo.addItem(Bundle.getMessage("ButtonSelect"));  // NOI18N
292                editCombo.addItem(Bundle.getMessage("ButtonEdit"));  // NOI18N
293                editCombo.addItem(Bundle.getMessage("BrowserButton"));  // NOI18N
294                editCombo.addItem(Bundle.getMessage("ButtonCopy"));  // NOI18N
295                editCombo.addItem(Bundle.getMessage("ButtonDelete"));  // NOI18N
296                editCombo.addItem(Bundle.getMessage("ButtonExportLogixToLogixNG"));  // NOI18N
297                TableColumn col = table.getColumnModel().getColumn(BeanTableDataModel.DELETECOL);
298                col.setCellEditor(new DefaultCellEditor(editCombo));
299            }
300
301            // Not needed - here for interface compatibility
302            @Override
303            public void clickOn(Logix t) {
304            }
305
306            @Override
307            public String getValue(String s) {
308                return "";
309            }
310
311            @Override
312            protected String getBeanType() {
313                return Bundle.getMessage("BeanNameLogix");  // NOI18N
314            }
315        };
316    }
317
318    /**
319     * Set title of Logix table.
320     */
321    @Override
322    protected void setTitle() {
323        f.setTitle(Bundle.getMessage("TitleLogixTable"));
324    }
325
326    /**
327     * Insert 2 table specific menus.
328     * <p>
329     * Accounts for the Window and Help menus, which are already added to the
330     * menu bar as part of the creation of the JFrame, by adding the new menus 2
331     * places earlier unless the table is part of the ListedTableFrame, which
332     * adds the Help menu later on.
333     *
334     * @param f the JFrame of this table
335     */
336    @Override
337    public void setMenuBar(BeanTableFrame<Logix> f) {
338        loadSelectionMode();
339        loadEditorMode();
340
341        JMenu menu = new JMenu(Bundle.getMessage("MenuOptions"));  // NOI18N
342        menu.setMnemonic(KeyEvent.VK_O);
343        javax.swing.JMenuBar menuBar = f.getJMenuBar();
344        int pos = menuBar.getMenuCount() - 1;  // count the number of menus to insert the TableMenus before 'Window' and 'Help'
345        int offset = 1;
346        log.debug("setMenuBar number of menu items = {}", pos);  // NOI18N
347        for (int i = 0; i <= pos; i++) {
348            if (menuBar.getComponent(i) instanceof JMenu) {
349                if (((JMenu) menuBar.getComponent(i)).getText().equals(Bundle.getMessage("MenuHelp"))) {  // NOI18N
350                    offset = -1;  // correct for use as part of ListedTableAction where the Help Menu is not yet present
351                }
352            }
353        }
354
355        ButtonGroup enableButtonGroup = new ButtonGroup();
356        JRadioButtonMenuItem r = new JRadioButtonMenuItem(Bundle.getMessage("EnableAll"));  // NOI18N
357        r.addActionListener(new ActionListener() {
358            @Override
359            public void actionPerformed(ActionEvent e) {
360                enableAll(true);
361            }
362        });
363        enableButtonGroup.add(r);
364        r.setSelected(true);
365        menu.add(r);
366
367        r = new JRadioButtonMenuItem(Bundle.getMessage("DisableAll"));  // NOI18N
368        r.addActionListener(new ActionListener() {
369            @Override
370            public void actionPerformed(ActionEvent e) {
371                enableAll(false);
372            }
373        });
374        enableButtonGroup.add(r);
375        menu.add(r);
376
377        menu.addSeparator();
378
379        ButtonGroup modeButtonGroup = new ButtonGroup();
380        r = new JRadioButtonMenuItem(Bundle.getMessage("UseMultiPick"));  // NOI18N
381        r.addItemListener(new ItemListener() {
382            @Override
383            public void itemStateChanged(ItemEvent e) {
384                setSelectionMode(SelectionMode.USEMULTI);
385            }
386        });
387        modeButtonGroup.add(r);
388        menu.add(r);
389        r.setSelected(_selectionMode == SelectionMode.USEMULTI);
390
391        r = new JRadioButtonMenuItem(Bundle.getMessage("UseSinglePick"));  // NOI18N
392        r.addItemListener(new ItemListener() {
393            @Override
394            public void itemStateChanged(ItemEvent e) {
395                setSelectionMode(SelectionMode.USESINGLE);
396            }
397        });
398        modeButtonGroup.add(r);
399        menu.add(r);
400        r.setSelected(_selectionMode == SelectionMode.USESINGLE);
401
402        r = new JRadioButtonMenuItem(Bundle.getMessage("UseComboNameBoxes"));  // NOI18N
403        r.addItemListener(new ItemListener() {
404            @Override
405            public void itemStateChanged(ItemEvent e) {
406                setSelectionMode(SelectionMode.USECOMBO);
407            }
408        });
409        modeButtonGroup.add(r);
410        menu.add(r);
411        r.setSelected(_selectionMode == SelectionMode.USECOMBO);
412
413        menu.addSeparator();
414
415        ButtonGroup viewButtonGroup = new ButtonGroup();
416        r = new JRadioButtonMenuItem(Bundle.getMessage("ListEdit"));  // NOI18N
417        r.addItemListener(new ItemListener() {
418            @Override
419            public void itemStateChanged(ItemEvent e) {
420                setEditorMode(EditMode.LISTEDIT);
421            }
422        });
423        viewButtonGroup.add(r);
424        menu.add(r);
425        r.setSelected(_editMode == EditMode.LISTEDIT);
426
427        r = new JRadioButtonMenuItem(Bundle.getMessage("TreeEdit"));  // NOI18N
428        r.addItemListener(new ItemListener() {
429            @Override
430            public void itemStateChanged(ItemEvent e) {
431                setEditorMode(EditMode.TREEEDIT);
432            }
433        });
434        viewButtonGroup.add(r);
435        menu.add(r);
436        r.setSelected(_editMode == EditMode.TREEEDIT);
437
438        menuBar.add(menu, pos + offset);
439
440        menu = new JMenu(Bundle.getMessage("MenuTools"));  // NOI18N
441        menu.setMnemonic(KeyEvent.VK_T);
442
443        JMenuItem item = new JMenuItem(Bundle.getMessage("OpenPickListTables"));  // NOI18N
444        item.addActionListener(new ActionListener() {
445            @Override
446            public void actionPerformed(ActionEvent e) {
447                openPickListTable();
448            }
449        });
450        menu.add(item);
451
452        item = new JMenuItem(Bundle.getMessage("FindOrphans"));  // NOI18N
453        item.addActionListener(new ActionListener() {
454            @Override
455            public void actionPerformed(ActionEvent e) {
456                findOrphansPressed(e);
457            }
458        });
459        menu.add(item);
460
461        item = new JMenuItem(Bundle.getMessage("EmptyConditionals"));  // NOI18N
462        item.addActionListener(new ActionListener() {
463            @Override
464            public void actionPerformed(ActionEvent e) {
465                findEmptyPressed(e);
466            }
467        });
468        menu.add(item);
469
470        item = new JMenuItem(Bundle.getMessage("CrossReference"));  // NOI18N
471        item.addActionListener(new ActionListener() {
472            BeanTableFrame<?> parent;
473
474            @Override
475            public void actionPerformed(ActionEvent e) {
476                new RefDialog(parent);
477            }
478
479            ActionListener init(BeanTableFrame<?> f) {
480                parent = f;
481                return this;
482            }
483        }.init(f));
484        menu.add(item);
485
486        item = new JMenuItem(Bundle.getMessage("DisplayWhereUsed"));  // NOI18N
487        item.addActionListener(new ActionListener() {
488            @Override
489            public void actionPerformed(ActionEvent e) {
490                makeWhereUsedWindow();
491            }
492        });
493        menu.add(item);
494
495        menuBar.add(menu, pos + offset + 1);  // add this menu to the right of the previous
496    }
497
498    /**
499     * Get the saved mode selection, default to the tranditional tabbed pick
500     * list.
501     * <p>
502     * During the menu build process, the corresponding menu item is set to
503     * selected.
504     *
505     * @since 4.7.3
506     */
507    void loadSelectionMode() {
508        Object modeName = InstanceManager.getDefault(jmri.UserPreferencesManager.class).
509                getProperty(getClassName(), "Selection Mode");      // NOI18N
510        if (modeName == null) {
511            _selectionMode = SelectionMode.USEMULTI;
512        } else {
513            String currentMode = (String) modeName;
514            switch (currentMode) {
515                case "USEMULTI":        // NOI18N
516                    _selectionMode = SelectionMode.USEMULTI;
517                    break;
518                case "USESINGLE":       // NOI18N
519                    _selectionMode = SelectionMode.USESINGLE;
520                    break;
521                case "USECOMBO":        // NOI18N
522                    _selectionMode = SelectionMode.USECOMBO;
523                    break;
524                default:
525                    log.warn("Invalid Logix conditional selection mode value, '{}', returned", currentMode);  // NOI18N
526                    _selectionMode = SelectionMode.USEMULTI;
527            }
528        }
529    }
530
531    /**
532     * Save the mode selection. Called by menu item change events.
533     *
534     * @since 4.7.3
535     * @param newMode The SelectionMode enum constant
536     */
537    void setSelectionMode(SelectionMode newMode) {
538        _selectionMode = newMode;
539        InstanceManager.getDefault(jmri.UserPreferencesManager.class).
540                setProperty(getClassName(), "Selection Mode", newMode.toString());  // NOI18N
541    }
542
543    /**
544     * Get the saved mode selection, default to the tranditional conditional
545     * list editor.
546     * <p>
547     * During the menu build process, the corresponding menu item is set to
548     * selected.
549     *
550     * @since 4.9.x
551     */
552    void loadEditorMode() {
553        Object modeName = InstanceManager.getDefault(jmri.UserPreferencesManager.class).
554                getProperty(getClassName(), "Edit Mode");      // NOI18N
555        if (modeName == null) {
556            _editMode = EditMode.LISTEDIT;
557        } else {
558            String currentMode = (String) modeName;
559            switch (currentMode) {
560                case "LISTEDIT":        // NOI18N
561                    _editMode = EditMode.LISTEDIT;
562                    break;
563                case "TREEEDIT":       // NOI18N
564                    _editMode = EditMode.TREEEDIT;
565                    break;
566                default:
567                    log.warn("Invalid conditional edit mode value, '{}', returned", currentMode);  // NOI18N
568                    _editMode = EditMode.LISTEDIT;
569            }
570        }
571    }
572
573    /**
574     * Save the view mode selection. Called by menu item change events.
575     *
576     * @since 4.9.x
577     * @param newMode The ViewMode enum constant
578     */
579    void setEditorMode(EditMode newMode) {
580        _editMode = newMode;
581        InstanceManager.getDefault(jmri.UserPreferencesManager.class).
582                setProperty(getClassName(), "Edit Mode", newMode.toString());  // NOI18N
583    }
584
585    /**
586     * Open a new Pick List to drag Actions from to form Logix Conditionals.
587     */
588    void openPickListTable() {
589        if (_pickTables == null) {
590            _pickTables = new jmri.jmrit.picker.PickFrame(Bundle.getMessage("TitlePickList"));  // NOI18N
591        } else {
592            _pickTables.setVisible(true);
593        }
594        _pickTables.toFront();
595    }
596
597    /**
598     * Find empty Conditional entries, called from menu.
599     *
600     * @see Maintenance#findEmptyPressed(java.awt.Frame)
601     * @param e the event heard
602     */
603    void findEmptyPressed(ActionEvent e) {
604        Maintenance.findEmptyPressed(f);
605    }
606
607    /**
608     * Find orphaned entries, called from menu.
609     *
610     * @see Maintenance#findOrphansPressed(java.awt.Frame)
611     * @param e the event heard
612     */
613    void findOrphansPressed(ActionEvent e) {
614        Maintenance.findOrphansPressed(f);
615    }
616
617    class RefDialog extends JDialog {
618
619        JTextField _devNameField;
620        java.awt.Frame _parent;
621
622        RefDialog(java.awt.Frame frame) {
623            super(frame, Bundle.getMessage("CrossReference"), true);  // NOI18N
624            _parent = frame;
625            JPanel extraPanel = new JPanel();
626            extraPanel.setLayout(new BoxLayout(extraPanel, BoxLayout.Y_AXIS));
627            _devNameField = new JTextField(30);
628            JPanel panel = makeEditPanel(_devNameField, "ElementName", "ElementNameHint");  // NOI18N
629            JButton referenceButton = new JButton(Bundle.getMessage("ReferenceButton"));  // NOI18N
630            panel.add(referenceButton);
631            referenceButton.addActionListener(new ActionListener() {
632                @Override
633                public void actionPerformed(ActionEvent e) {
634                    deviceReportPressed(e);
635                }
636            });
637            panel.add(referenceButton);
638            extraPanel.add(panel);
639            setContentPane(extraPanel);
640            pack();
641            // setLocationRelativeTo((java.awt.Component)_pos);
642            setVisible(true);
643        }
644
645        void deviceReportPressed(ActionEvent e) {
646            Maintenance.deviceReportPressed(_devNameField.getText(), _parent);
647            dispose();
648        }
649    }
650
651    void enableAll(boolean enable) {
652        for (Logix x : _logixManager.getNamedBeanSet()) {
653            x.setEnabled(enable);
654        }
655    }
656
657    @Override
658    protected String helpTarget() {
659        return "package.jmri.jmrit.beantable.LogixTable";  // NOI18N
660    }
661
662    // ------------ variable definitions ------------
663
664    // Multi use variables
665    ConditionalManager _conditionalManager = null;  // set when LogixAction is created
666    LogixManager _logixManager = null;  // set when LogixAction is created
667
668    ConditionalEditBase _baseEdit;
669
670    boolean _showReminder = false;
671    private boolean _checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled();
672    jmri.jmrit.picker.PickFrame _pickTables;
673
674    // Current focus variables
675    Logix _curLogix = null;
676    int conditionalRowNumber = 0;
677
678    // Add Logix Variables
679    JmriJFrame addLogixFrame = null;
680    JTextField _systemName = new JTextField(20);
681    JTextField _addUserName = new JTextField(20);
682    JComboBox<String> _copyCombo = new JComboBox<>();
683
684    JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName"));   // NOI18N
685    JLabel _sysNameLabel = new JLabel(Bundle.getMessage("BeanNameLogix") + " " + Bundle.getMessage("ColumnSystemName") + ":");  // NOI18N
686    JLabel _userNameLabel = new JLabel(Bundle.getMessage("BeanNameLogix") + " " + Bundle.getMessage("ColumnUserName") + ":");   // NOI18N
687    String systemNameAuto = this.getClass().getName() + ".AutoSystemName";      // NOI18N
688    JButton create;
689
690    // Edit Logix Variables
691    private boolean _inEditMode = false;
692    private boolean _inCopyMode = false;
693    private boolean _inAddMode = false;
694
695    /**
696     * Input selection names.
697     *
698     * @since 4.7.3
699     */
700    public enum SelectionMode {
701        /**
702         * Use the traditional text field, with the tabbed Pick List available
703         * for drag-n-drop
704         */
705        USEMULTI,
706        /**
707         * Use the traditional text field, but with a single Pick List that
708         * responds with a click
709         */
710        USESINGLE,
711        /**
712         * Use combo boxes to select names instead of a text field.
713         */
714        USECOMBO;
715    }
716    SelectionMode _selectionMode;
717
718    /**
719     * Conditional edit view mode
720     *
721     * @since 4.9.x
722     */
723    public enum EditMode {
724        /**
725         * Use the traditional table list mode for editing conditionals
726         */
727        LISTEDIT,
728        /**
729         * Use the tree based mode for editing condtiionals
730         */
731        TREEEDIT;
732    }
733    EditMode _editMode;
734
735    // Save conditional reference target names before updating
736    private TreeSet<String> _saveTargetNames = new TreeSet<String>();
737    private HashMap<String, ArrayList<String>> _saveTargetList = new HashMap<>();
738
739    // ------------ Methods for Add Logix Window ------------
740
741    /**
742     * Respond to the Add button in Logix table Creates and/or initialize the
743     * Add Logix pane.
744     *
745     * @param e The event heard
746     */
747    @Override
748    protected void addPressed(ActionEvent e) {
749        // possible change
750        if (!checkFlags(null)) {
751            return;
752        }
753        _showReminder = true;
754        // make an Add Logix Frame
755        if (addLogixFrame == null) {
756            JPanel panel5 = makeAddLogixFrame("TitleAddLogix", "AddLogixMessage",
757                    "package.jmri.jmrit.beantable.LogixAddEdit");  // NOI18N
758            // Create Logix
759            create = new JButton(Bundle.getMessage("ButtonCreate"));  // NOI18N
760            panel5.add(create);
761            create.addActionListener(new ActionListener() {
762                @Override
763                public void actionPerformed(ActionEvent e) {
764                    createPressed(e);
765                }
766            });
767            create.setToolTipText(Bundle.getMessage("LogixCreateButtonHint"));  // NOI18N
768        }
769        _inAddMode = true;
770        addLogixFrame.setEscapeKeyClosesWindow(true);
771        addLogixFrame.getRootPane().setDefaultButton(create);
772        addLogixFrame.pack();
773        addLogixFrame.setVisible(true);
774        _autoSystemName.setSelected(false);
775        addLogixFrame.setLocationRelativeTo(getFrame());
776        InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
777            _autoSystemName.setSelected(prefMgr.getSimplePreferenceState(systemNameAuto));
778        });
779    }
780
781    /**
782     * Create or copy Logix frame.
783     *
784     * @param titleId   property key to fetch as title of the frame (using Bundle)
785     * @param messageId part 1 of property key to fetch as user instruction on
786     *                  pane, either 1 or 2 is added to form the whole key
787     * @param helpFile help file name
788     * @return the button JPanel
789     */
790    JPanel makeAddLogixFrame(String titleId, String messageId, String helpFile) {
791        addLogixFrame = new JmriJFrame(Bundle.getMessage(titleId));
792        addLogixFrame.addHelpMenu(helpFile, true);     // NOI18N
793        addLogixFrame.setLocation(50, 30);
794        Container contentPane = addLogixFrame.getContentPane();
795        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
796        JPanel p;
797        p = new JPanel();
798        p.setLayout(new FlowLayout());
799        p.setLayout(new java.awt.GridBagLayout());
800        java.awt.GridBagConstraints c = new java.awt.GridBagConstraints();
801        c.gridwidth = 1;
802        c.gridheight = 1;
803        c.gridx = 0;
804        c.gridy = 0;
805        c.anchor = java.awt.GridBagConstraints.EAST;
806        p.add(_sysNameLabel, c);
807        c.gridy = 1;
808        p.add(_userNameLabel, c);
809        c.gridx = 1;
810        c.gridy = 0;
811        c.anchor = java.awt.GridBagConstraints.WEST;
812        c.weightx = 1.0;
813        c.fill = java.awt.GridBagConstraints.HORIZONTAL;  // text field will expand
814        if (titleId.equals("TitleCopyLogix")) {
815            p.add(_copyCombo, c);
816        } else {
817            p.add(_systemName, c);
818        }
819        c.gridy = 1;
820        p.add(_addUserName, c);
821        c.gridx = 2;
822        c.gridy = 1;
823        c.anchor = java.awt.GridBagConstraints.WEST;
824        c.weightx = 1.0;
825        c.fill = java.awt.GridBagConstraints.HORIZONTAL;  // text field will expand
826        c.gridy = 0;
827        p.add(_autoSystemName, c);
828        _addUserName.setToolTipText(Bundle.getMessage("LogixUserNameHint"));    // NOI18N
829        _systemName.setToolTipText(Bundle.getMessage("LogixSystemNameHint"));   // NOI18N
830        contentPane.add(p);
831        // set up message
832        JPanel panel3 = new JPanel();
833        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
834        JPanel panel31 = new JPanel();
835        panel31.setLayout(new FlowLayout());
836        JLabel message1 = new JLabel(Bundle.getMessage(messageId + "1"));  // NOI18N
837        panel31.add(message1);
838        JPanel panel32 = new JPanel();
839        JLabel message2 = new JLabel(Bundle.getMessage(messageId + "2"));  // NOI18N
840        panel32.add(message2);
841        panel3.add(panel31);
842        panel3.add(panel32);
843        contentPane.add(panel3);
844
845        // set up create and cancel buttons
846        JPanel panel5 = new JPanel();
847        panel5.setLayout(new FlowLayout());
848        // Cancel
849        JButton cancel = new JButton(Bundle.getMessage("ButtonCancel"));    // NOI18N
850        panel5.add(cancel);
851        cancel.addActionListener(new ActionListener() {
852            @Override
853            public void actionPerformed(ActionEvent e) {
854                cancelAddPressed(e);
855            }
856        });
857        cancel.setToolTipText(Bundle.getMessage("CancelLogixButtonHint"));      // NOI18N
858
859        addLogixFrame.addWindowListener(new java.awt.event.WindowAdapter() {
860            @Override
861            public void windowClosing(java.awt.event.WindowEvent e) {
862                cancelAddPressed(null);
863            }
864        });
865        contentPane.add(panel5);
866
867        _autoSystemName.addItemListener(
868                new ItemListener() {
869            @Override
870            public void itemStateChanged(ItemEvent e) {
871                autoSystemName();
872            }
873        });
874        return panel5;
875    }
876
877    /**
878     * Enable/disable fields for data entry when user selects to have system
879     * name automatically generated.
880     */
881    void autoSystemName() {
882        if (_autoSystemName.isSelected()) {
883            _systemName.setEnabled(false);
884            _sysNameLabel.setEnabled(false);
885        } else {
886            _systemName.setEnabled(true);
887            _sysNameLabel.setEnabled(true);
888        }
889    }
890
891    /**
892     * Respond to the Cancel button in Add Logix window.
893     * <p>
894     * Note: Also get there if the user closes the Add Logix window.
895     *
896     * @param e The event heard
897     */
898    void cancelAddPressed(ActionEvent e) {
899        addLogixFrame.setVisible(false);
900        addLogixFrame.dispose();
901        addLogixFrame = null;
902        _inAddMode = false;
903        _inCopyMode = false;
904        if (f != null) {
905            f.setVisible(true);
906        }
907    }
908
909    /**
910     * Respond to the Copy Logix button in Add Logix window.
911     * <p>
912     * Provides a pane to set new properties of the copy.
913     *
914     * @param sName system name of Logix to be copied
915     */
916    void copyPressed(String sName) {
917        if (!checkFlags(sName)) {
918            return;
919        }
920        _showReminder = true;
921
922        // Refresh combo box Logix list
923        _copyCombo.removeActionListener(this::copyComboListener);
924        _copyCombo.removeAllItems();
925        _copyCombo.addItem("");
926        var logixList = InstanceManager.getDefault(LogixManager.class).getNamedBeanSet();
927        logixList.forEach((lgx) -> {
928            _copyCombo.addItem(lgx.getSystemName());
929        });
930        _copyCombo.setEditable(true);
931        _copyCombo.setSelectedIndex(0);
932        _copyCombo.addActionListener(this::copyComboListener);
933        jmri.util.swing.JComboBoxUtil.setupComboBoxMaxRows(_copyCombo);
934
935        // make an Add Logix Frame
936        if (addLogixFrame == null) {
937            JPanel panel5 = makeAddLogixFrame("TitleCopyLogix", "CopyLogixMessage",
938                    "package.jmri.jmrit.conditional.ConditionalCopy");    // NOI18N
939            // Create Logix
940            JButton create = new JButton(Bundle.getMessage("ButtonCopy"));  // NOI18N
941            panel5.add(create);
942            create.addActionListener(new CopyAction(sName));
943            addLogixFrame.pack();
944            addLogixFrame.setVisible(true);
945            _autoSystemName.setSelected(false);
946            addLogixFrame.setLocationRelativeTo(getFrame());
947            InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
948                _autoSystemName.setSelected(prefMgr.getSimplePreferenceState(systemNameAuto));
949            });
950            _inCopyMode = true;
951        }
952        _curLogix = _logixManager.getBySystemName(sName);
953    }
954
955    class CopyAction implements ActionListener {
956        String _lgxName;
957        CopyAction(String lgxName) {
958            _lgxName = lgxName;
959        }
960        @Override
961        public void actionPerformed(ActionEvent e) {
962            copyLogixPressed(_lgxName);
963        }
964    }
965
966    /**
967     * Copy the Logix as configured in the Copy set up pane.
968     *
969     * @param lgxName Logix system name to be copied
970     */
971    private void copyLogixPressed(String lgxName) {
972        _systemName.setText((String) _copyCombo.getSelectedItem());
973        String uName = _addUserName.getText();
974        if (uName.length() == 0) {
975            uName = null;
976        }
977        Logix targetLogix;
978        if (_autoSystemName.isSelected()) {
979            if (!checkLogixUserName(uName)) {
980                return;
981            }
982            targetLogix = _logixManager.createNewLogix(uName);
983        } else {
984            // Validate the system name
985            if (!checkLogixSysName()) {
986                cancelAddPressed(null);
987                return;
988            }
989            var sName = _systemName.getText();  // Use the validated, possibly changed, system name
990
991            targetLogix = _logixManager.getBySystemName(sName);
992            if (targetLogix == null && uName != null) {
993                targetLogix = _logixManager.getByUserName(uName);
994            }
995            if (targetLogix != null) {
996                int result = JmriJOptionPane.showConfirmDialog(f,
997                        Bundle.getMessage("ConfirmLogixDuplicate",
998                                targetLogix.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME), lgxName), // NOI18N
999                        Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,    // NOI18N
1000                        JmriJOptionPane.QUESTION_MESSAGE);
1001                if (JmriJOptionPane.YES_OPTION != result) {
1002                    return;
1003                }
1004            }
1005            if (targetLogix == null) {
1006                targetLogix = _logixManager.createNewLogix(sName, uName);
1007                if (targetLogix == null) {
1008                    // should never get here unless there is an assignment conflict
1009                    log.error("Failure to create Logix with System Name: {}", sName);  // NOI18N
1010                    return;
1011                }
1012            } else {
1013                targetLogix.setUserName(uName);
1014            }
1015        }
1016        cancelAddPressed(null);
1017        _baseEdit = new ConditionalListCopy(lgxName, targetLogix);
1018        _baseEdit.locateAt(getFrame());
1019        _inCopyMode = true;
1020        _baseEdit.addLogixEventListener(new ConditionalBaseListener(lgxName));
1021    }
1022
1023    /**
1024     * Set the user name input field.
1025     * @param e The action event.
1026     */
1027    private void copyComboListener(ActionEvent e) {
1028        if (!e.getActionCommand().equals("comboBoxChanged")) {
1029            return;
1030        }
1031
1032        var name = "";
1033        var index = _copyCombo.getSelectedIndex();
1034        if (index > 0) {
1035            var logix = _logixManager.getLogix(_copyCombo.getItemAt(index));
1036            if (logix != null) {
1037                var userName = logix.getUserName();
1038                if (userName != null) {
1039                    name = userName;
1040                }
1041            }
1042        }
1043        _addUserName.setText(name);
1044    }
1045
1046    /**
1047     * Check and warn if a string is already in use as the user name of a Logix.
1048     *
1049     * @param uName the suggested name
1050     * @return true if not in use
1051     */
1052    boolean checkLogixUserName(String uName) {
1053        // check if a Logix with the same user name exists
1054        if (uName != null && uName.trim().length() > 0) {
1055            Logix x = _logixManager.getByUserName(uName);
1056            if (x != null) {
1057                // Logix with this user name already exists
1058                JmriJOptionPane.showMessageDialog(getFrame(),
1059                        Bundle.getMessage("LogixError3"), Bundle.getMessage("ErrorTitle"),
1060                        JmriJOptionPane.ERROR_MESSAGE);
1061                return false;
1062            }
1063        }
1064        return true;
1065    }
1066
1067    /**
1068     * Check for a valid Logix system name.
1069     * A valid name starts with the Logix prefix consisting of the Internal system prefix (normally I) + X,
1070     * and at least 1 additional character. The prefix will be added if necessary.
1071     * Any makeSystemName errors are logged to the system console and a dialog is displayed.
1072     * @return true if the name is now valid.
1073     */
1074    boolean checkLogixSysName() {
1075        String sName = _systemName.getText();
1076
1077        try {
1078            sName = InstanceManager.getDefault(jmri.LogixManager.class).makeSystemName(sName);
1079        } catch (jmri.NamedBean.BadSystemNameException ex) {
1080            JmriJOptionPane.showMessageDialog(getFrame(),
1081                    Bundle.getMessage("LogixError8"), Bundle.getMessage("ErrorTitle"),
1082                    JmriJOptionPane.ERROR_MESSAGE);
1083            return false;
1084        }
1085        _systemName.setText(sName);
1086        return true;
1087    }
1088
1089    /**
1090     * Check if another Logix editing session is currently open or no system
1091     * name is provided.
1092     *
1093     * @param sName system name of Logix to be copied
1094     * @return true if a new session may be started
1095     */
1096    boolean checkFlags(String sName) {
1097        if (_inEditMode) {
1098            // Already editing a Logix, ask for completion of that edit
1099            JmriJOptionPane.showMessageDialog(getFrame(),
1100                    Bundle.getMessage("LogixError32", _curLogix.getSystemName()),
1101                    Bundle.getMessage("ErrorTitle"),
1102                    JmriJOptionPane.ERROR_MESSAGE);
1103            _baseEdit.bringToFront();
1104            return false;
1105        }
1106
1107        if (_inAddMode) {
1108            // Adding a Logix, ask for completion of that edit
1109            JmriJOptionPane.showMessageDialog(getFrame(),
1110                    Bundle.getMessage("LogixError33"),
1111                    Bundle.getMessage("ErrorTitle"), // NOI18N
1112                    JmriJOptionPane.ERROR_MESSAGE);
1113            addLogixFrame.toFront();
1114            return false;
1115        }
1116
1117        if (_inCopyMode) {
1118            // Already copying a Logix, ask for completion of that edit
1119            JmriJOptionPane.showMessageDialog(getFrame(),
1120                    Bundle.getMessage("LogixError31", _curLogix.getSystemName()),
1121                    Bundle.getMessage("ErrorTitle"), // NOI18N
1122                    JmriJOptionPane.ERROR_MESSAGE);
1123            _baseEdit.bringToFront();
1124            return false;
1125        }
1126
1127        if (sName != null) {
1128            // check if a Logix with this name exists
1129            Logix x = _logixManager.getBySystemName(sName);
1130            if (x == null) {
1131                // Logix does not exist, so cannot be edited
1132                log.error("No Logix with system name: {}", sName);
1133                JmriJOptionPane.showMessageDialog(getFrame(),
1134                        Bundle.getMessage("LogixError5"),
1135                        Bundle.getMessage("ErrorTitle"), // NOI18N
1136                        JmriJOptionPane.ERROR_MESSAGE);
1137                return false;
1138            }
1139        }
1140        return true;
1141    }
1142
1143    /**
1144     * Respond to the Create Logix button in Add Logix window.
1145     *
1146     * @param e The event heard
1147     */
1148    void createPressed(ActionEvent e) {
1149        // possible change
1150        _showReminder = true;
1151        String sName = "";
1152        String uName = _addUserName.getText();
1153        if (uName.length() == 0) {
1154            uName = null;
1155        }
1156        if (_autoSystemName.isSelected()) {
1157            if (!checkLogixUserName(uName)) {
1158                return;
1159            }
1160            _curLogix = _logixManager.createNewLogix(uName);
1161            sName = _curLogix.getSystemName();
1162        } else {
1163            if (!checkLogixSysName()) {
1164                return;
1165            }
1166            // Get validated system name
1167            sName = _systemName.getText();
1168            // check if a Logix with this name already exists
1169            Logix x = null;
1170            try {
1171                x = _logixManager.getBySystemName(sName);
1172            } catch (Exception ex) {
1173                // user input no good
1174                handleCreateException(sName);
1175                return;  // without creating
1176            }
1177            if (x != null) {
1178                // Logix already exists
1179                JmriJOptionPane.showMessageDialog(getFrame(), Bundle.getMessage("LogixError1"),
1180                        Bundle.getMessage("ErrorTitle"), // NOI18N
1181                        JmriJOptionPane.ERROR_MESSAGE);
1182                return;
1183            }
1184            if (!checkLogixUserName(uName)) {
1185                return;
1186            }
1187            // Create the new Logix
1188            _curLogix = _logixManager.createNewLogix(sName, uName);
1189            if (_curLogix == null) {
1190                // should never get here unless there is an assignment conflict
1191                log.error("Failure to create Logix with System Name: {}", sName);  // NOI18N
1192                return;
1193            }
1194        }
1195        cancelAddPressed(null);
1196        // create the Edit Logix Window
1197        editPressed(sName);
1198        InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
1199            prefMgr.setSimplePreferenceState(systemNameAuto, _autoSystemName.isSelected());
1200        });
1201    }
1202
1203    void handleCreateException(String sysName) {
1204        JmriJOptionPane.showMessageDialog(getFrame(),
1205                Bundle.getMessage("ErrorLogixAddFailed", sysName), // NOI18N
1206                Bundle.getMessage("ErrorTitle"), // NOI18N
1207                JmriJOptionPane.ERROR_MESSAGE);
1208    }
1209
1210    // ------------ Methods for Edit Logix Pane ------------
1211
1212    /**
1213     * Respond to the Edit button pressed in Logix table.
1214     *
1215     * @param sName system name of Logix to be edited
1216     */
1217    void editPressed(String sName) {
1218        if (!checkFlags(sName)) {
1219            return;
1220        }
1221
1222        if (sName.equals(SensorGroupFrame.logixSysName)) {
1223            // Sensor group message
1224            JmriJOptionPane.showMessageDialog(getFrame(),
1225                    Bundle.getMessage("LogixWarn8", SensorGroupFrame.logixUserName, SensorGroupFrame.logixSysName),
1226                    Bundle.getMessage("WarningTitle"), // NOI18N
1227                    JmriJOptionPane.WARNING_MESSAGE);
1228            return;
1229        }
1230        _curLogix = _logixManager.getBySystemName(sName);
1231
1232        // Create a new conditional edit view, add the listener.
1233        if (_editMode == EditMode.TREEEDIT) {
1234            _baseEdit = new ConditionalTreeEdit(sName);
1235        } else {
1236            _baseEdit = new ConditionalListEdit(sName);
1237        }
1238        _baseEdit.locateAt(getFrame());
1239        _inEditMode = true;
1240        _baseEdit.addLogixEventListener(new ConditionalBaseListener(sName));
1241    }
1242
1243    class ConditionalBaseListener implements ConditionalEditBase.LogixEventListener {
1244        String _lgxName;
1245        ConditionalBaseListener(String lgxName) {
1246            _lgxName = lgxName;
1247        }
1248
1249        @Override
1250        public void logixEventOccurred() {
1251            _baseEdit.logixData.forEach((key, value) -> {
1252                if (key.equals("Finish")) {                  // NOI18N
1253                    _baseEdit = null;
1254                    _inEditMode = false;
1255                    _inCopyMode = false;
1256                    Logix x = _logixManager.getBySystemName(value);
1257                    if (x == null) {
1258                        log.error("Found no logix for name {} when done", value);
1259                        return;
1260                    }
1261                    x.activateLogix();
1262                    f.setVisible(true);
1263                } else if (key.equals("Delete")) {           // NOI18N
1264                    deletePressed(value);
1265                } else if (key.equals("chgUname")) {         // NOI18N
1266                    Logix x = _logixManager.getBySystemName(_lgxName);
1267                    if (x == null) {
1268                        log.error("Found no logix for name {} when changing user name (2)", _lgxName);
1269                        return;
1270                    }
1271                    x.setUserName(value);
1272                    m.fireTableDataChanged();
1273                }
1274            });
1275        }
1276    }
1277
1278    /**
1279     * Display reminder to save.
1280     */
1281    void showSaveReminder() {
1282        if (_showReminder && !_checkEnabled) {
1283            if (InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class) != null) {
1284                InstanceManager.getDefault(jmri.UserPreferencesManager.class).
1285                        showInfoMessage(Bundle.getMessage("ReminderTitle"), Bundle.getMessage("ReminderSaveString", Bundle.getMessage("MenuItemLogixTable")), // NOI18N
1286                                getClassName(),
1287                                "remindSaveLogix");  // NOI18N
1288            }
1289        }
1290    }
1291
1292    @Override
1293    public void setMessagePreferencesDetails() {
1294        HashMap<Integer, String> options = new HashMap< Integer, String>(3);
1295        options.put(0x00, Bundle.getMessage("DeleteAsk"));      // NOI18N
1296        options.put(0x01, Bundle.getMessage("DeleteNever"));    // NOI18N
1297        options.put(0x02, Bundle.getMessage("DeleteAlways"));   // NOI18N
1298        jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class).setMessageItemDetails(getClassName(), "delete", Bundle.getMessage("DeleteLogix"), options, 0x00);  // NOI18N
1299        jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class).setPreferenceItemDetails(getClassName(), "remindSaveLogix", Bundle.getMessage("HideSaveReminder"));  // NOI18N
1300        super.setMessagePreferencesDetails();
1301    }
1302
1303    /**
1304     * Respond to the Delete combo selection Logix window or conditional view
1305     * delete request.
1306     *
1307     * @param sName system name of bean to be deleted
1308     */
1309    void deletePressed(String sName) {
1310        if (!checkConditionalReferences(sName)) {
1311            return;
1312        }
1313        final Logix x = _logixManager.getBySystemName(sName);
1314        final jmri.UserPreferencesManager p;
1315        p = jmri.InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class);
1316        if (p != null && p.getMultipleChoiceOption(getClassName(), "delete") == 0x02) {     // NOI18N
1317            if (x != null) {
1318                _logixManager.deleteLogix(x);
1319                deleteSourceWhereUsed();
1320            }
1321        } else {
1322            final JDialog dialog = new JDialog();
1323            String msg;
1324            dialog.setTitle(Bundle.getMessage("QuestionTitle"));     // NOI18N
1325            dialog.setLocationRelativeTo(getFrame());
1326            dialog.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
1327            JPanel container = new JPanel();
1328            container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
1329            container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
1330            msg = Bundle.getMessage("ConfirmLogixDelete", sName);    // NOI18N
1331            JLabel question = new JLabel(msg);
1332            question.setAlignmentX(Component.CENTER_ALIGNMENT);
1333            container.add(question);
1334
1335            final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting"));  // NOI18N
1336            remember.setFont(remember.getFont().deriveFont(10f));
1337            remember.setAlignmentX(Component.CENTER_ALIGNMENT);
1338
1339            JButton yesButton = new JButton(Bundle.getMessage("ButtonYes"));    // NOI18N
1340            JButton noButton = new JButton(Bundle.getMessage("ButtonNo"));      // NOI18N
1341            JPanel button = new JPanel();
1342            button.setAlignmentX(Component.CENTER_ALIGNMENT);
1343            button.add(yesButton);
1344            button.add(noButton);
1345            container.add(button);
1346
1347            noButton.addActionListener(new ActionListener() {
1348                @Override
1349                public void actionPerformed(ActionEvent e) {
1350                    //there is no point in remebering this the user will never be
1351                    //able to delete a bean!
1352                    /*if(remember.isSelected()){
1353                     setDisplayDeleteMsg(0x01);
1354                     }*/
1355                    dialog.dispose();
1356                }
1357            });
1358
1359            yesButton.addActionListener(new ActionListener() {
1360                @Override
1361                public void actionPerformed(ActionEvent e) {
1362                    if (p != null && remember.isSelected()) {
1363                        p.setMultipleChoiceOption(getClassName(), "delete", 0x02);  // NOI18N
1364                    }
1365                    if (x != null) {
1366                        _logixManager.deleteLogix(x);
1367                        deleteSourceWhereUsed();
1368                    }
1369                    dialog.dispose();
1370                }
1371            });
1372            container.add(remember);
1373            container.setAlignmentX(Component.CENTER_ALIGNMENT);
1374            container.setAlignmentY(Component.CENTER_ALIGNMENT);
1375            dialog.getContentPane().add(container);
1376            dialog.pack();
1377            dialog.setModal(true);
1378            dialog.setVisible(true);
1379        }
1380
1381        f.setVisible(true);
1382    }
1383
1384    /**
1385     * Respond to the Export to LogixNG combo selection Logix window request.
1386     *
1387     * @param sName system name of bean to export
1388     */
1389    void exportToLogixNGPressed(String sName) {
1390        if (!checkConditionalReferences(sName)) {
1391            return;
1392        }
1393        final Logix logix = _logixManager.getBySystemName(sName);
1394        if (logix == null) throw new NullPointerException("logix is null");
1395
1396        boolean error = false;
1397        StringBuilder errorMessage = new StringBuilder("<html><table border=\"1\" cellspacing=\"0\" cellpadding=\"2\">");
1398        errorMessage.append("<tr><th>");
1399        errorMessage.append(Bundle.getMessage("ColumnSystemName"));
1400        errorMessage.append("</th><th>");
1401        errorMessage.append(Bundle.getMessage("ColumnUserName"));
1402        errorMessage.append("</th><th>");
1403        errorMessage.append(Bundle.getMessage("ExportLogixColumnError"));
1404        errorMessage.append("</th></tr>");
1405
1406        try {
1407            ImportLogix importLogix = new ImportLogix(logix, true, true);
1408            importLogix.doImport();
1409        } catch (JmriException e) {
1410            errorMessage.append("<tr><td>");
1411            errorMessage.append(logix.getSystemName());
1412            errorMessage.append("</td><td>");
1413            errorMessage.append(logix.getUserName() != null ? logix.getUserName() : "");
1414            errorMessage.append("</td><td>");
1415            errorMessage.append(e.getMessage());
1416            errorMessage.append("</td></tr>");
1417            log.error("Error thrown: {}", e, e);
1418            error = true;
1419        }
1420
1421        if (!error) {
1422            try {
1423                ImportLogix importLogix = new ImportLogix(logix, true, false);
1424                importLogix.doImport();
1425                JmriJOptionPane.showMessageDialog(f, Bundle.getMessage("LogixIsExported", logix.getDisplayName()), Bundle.getMessage("TitleLogixExportSuccess"), JmriJOptionPane.INFORMATION_MESSAGE);
1426            } catch (JmriException e) {
1427                throw new RuntimeException("Unexpected error: "+e.getMessage(), e);
1428            }
1429        } else {
1430            errorMessage.append("</table></html>");
1431            JmriJOptionPane.showMessageDialog(f, errorMessage.toString(), Bundle.getMessage("TitleLogixExportError"), JmriJOptionPane.ERROR_MESSAGE);
1432        }
1433    }
1434
1435    /**
1436     * Build a tree set from conditional references.
1437     *
1438     * @since 4.7.4
1439     * @param varList The ConditionalVariable list that might contain
1440     *                conditional references
1441     * @param treeSet A tree set to be built from the varList data
1442     */
1443    void loadReferenceNames(List<ConditionalVariable> varList, TreeSet<String> treeSet) {
1444        treeSet.clear();
1445        for (ConditionalVariable var : varList) {
1446            if (var.getType() == Conditional.Type.CONDITIONAL_TRUE
1447                    || var.getType() == Conditional.Type.CONDITIONAL_FALSE) {
1448                treeSet.add(var.getName());
1449            }
1450        }
1451    }
1452
1453    boolean checkConditionalUserName(String uName, Logix logix) {
1454        if ((uName != null) && (!(uName.equals("")))) {
1455            Conditional p = _conditionalManager.getByUserName(logix, uName);
1456            if (p != null) {
1457                // Conditional with this user name already exists
1458                log.error("Failure to update Conditional with Duplicate User Name: {}", uName);
1459                JmriJOptionPane.showMessageDialog(getFrame(),
1460                        Bundle.getMessage("LogixError10"), // NOI18N
1461                        Bundle.getMessage("ErrorTitle"), // NOI18N
1462                        JmriJOptionPane.ERROR_MESSAGE);
1463                return false;
1464            }
1465        }
1466        return true;
1467    }
1468
1469    /**
1470     * Check form of Conditional systemName.
1471     *
1472     * @param sName system name of bean to be checked
1473     * @return false if sName is empty string or null
1474     */
1475    boolean checkConditionalSystemName(String sName) {
1476        if ((sName != null) && (!(sName.equals("")))) {
1477            Conditional p = _conditionalManager.getBySystemName(sName);
1478            if (p != null) {
1479                return false;
1480            }
1481        } else {
1482            return false;
1483        }
1484        return true;
1485    }
1486
1487    /**
1488     * Check for conditional references.
1489     *
1490     * @since 4.7.4
1491     * @param logixName The Logix under consideration
1492     * @return true if no references
1493     */
1494    boolean checkConditionalReferences(String logixName) {
1495        _saveTargetList.clear();
1496        Logix x = _logixManager.getLogix(logixName);
1497        int numConditionals = x.getNumConditionals();
1498        if (numConditionals > 0) {
1499            for (int i = 0; i < numConditionals; i++) {
1500                String csName = x.getConditionalByNumberOrder(i);
1501
1502                // If the conditional is a where used source, retain it for later
1503                ArrayList<String> targetList = InstanceManager.getDefault(jmri.ConditionalManager.class).getTargetList(csName);
1504                if (targetList.size() > 0) {
1505                    _saveTargetList.put(csName, targetList);
1506                }
1507
1508                // If the conditional is a where used target, check scope
1509                ArrayList<String> refList = InstanceManager.getDefault(jmri.ConditionalManager.class).getWhereUsed(csName);
1510                if (refList != null) {
1511                    for (String refName : refList) {
1512                        Logix xRef = _conditionalManager.getParentLogix(refName);
1513                        String xsName = xRef.getSystemName();
1514                        if (logixName.equals(xsName)) {
1515                            // Member of the same Logix
1516                            continue;
1517                        }
1518
1519                        // External references have to be removed before the Logix can be deleted.
1520                        Conditional c = x.getConditional(csName);
1521                        Conditional cRef = xRef.getConditional(refName);
1522                        JmriJOptionPane.showMessageDialog(getFrame(),
1523                                Bundle.getMessage("LogixError11", c.getUserName(), c.getSystemName(), cRef.getUserName(),
1524                                        cRef.getSystemName(), xRef.getUserName(), xRef.getSystemName()), // NOI18N
1525                                Bundle.getMessage("ErrorTitle"),
1526                                JmriJOptionPane.ERROR_MESSAGE);  // NOI18N
1527                        return false;
1528                    }
1529                }
1530            }
1531        }
1532        return true;
1533    }
1534
1535    /**
1536     * Remove target/source where used entries after a Logix delete.
1537     *
1538     * @since 4.7.4
1539     */
1540    void deleteSourceWhereUsed() {
1541        _saveTargetList.forEach((refName, targetList) -> {
1542            for (String targetName : targetList) {
1543                InstanceManager.getDefault(jmri.ConditionalManager.class).removeWhereUsed(targetName, refName);
1544            }
1545        });
1546    }
1547
1548    /**
1549     * Update the conditional reference where used.
1550     * <p>
1551     * The difference between the saved target names and new target names is
1552     * used to add/remove where used references.
1553     *
1554     * @since 4.7.4
1555     * @param newTargetNames The conditional target names after updating
1556     * @param refName        The system name for the referencing conditional
1557     */
1558    void updateWhereUsed(TreeSet<String> newTargetNames, String refName) {
1559        TreeSet<String> deleteNames = new TreeSet<>(_saveTargetNames);
1560        deleteNames.removeAll(newTargetNames);
1561        for (String deleteName : deleteNames) {
1562            InstanceManager.getDefault(jmri.ConditionalManager.class).removeWhereUsed(deleteName, refName);
1563        }
1564
1565        TreeSet<String> addNames = new TreeSet<>(newTargetNames);
1566        addNames.removeAll(_saveTargetNames);
1567        for (String addName : addNames) {
1568            InstanceManager.getDefault(jmri.ConditionalManager.class).addWhereUsed(addName, refName);
1569        }
1570    }
1571
1572    /**
1573     * Create Variable and Action editing pane center part.
1574     *
1575     * @param comp  Field or comboBox to include on sub pane
1576     * @param label property key for label
1577     * @param hint  property key for tooltip for this sub pane
1578     * @return JPanel containing interface
1579     */
1580    JPanel makeEditPanel(JComponent comp, String label, String hint) {
1581        JPanel panel = new JPanel();
1582        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
1583        JPanel p = new JPanel();
1584        p.add(new JLabel(Bundle.getMessage(label)));
1585        panel.add(p);
1586        if (hint != null) {
1587            panel.setToolTipText(Bundle.getMessage(hint));
1588        }
1589        comp.setMaximumSize(comp.getPreferredSize());  // override for text fields
1590        panel.add(comp);
1591        panel.add(Box.createVerticalGlue());
1592        return panel;
1593    }
1594
1595    /**
1596     * Format time to hh:mm given integer hour and minute.
1597     *
1598     * @param hour   value for time hours
1599     * @param minute value for time minutes
1600     * @return Formatted time string
1601     */
1602    public static String formatTime(int hour, int minute) {
1603        String s = "";
1604        String t = Integer.toString(hour);
1605        if (t.length() == 2) {
1606            s = t + ":";
1607        } else if (t.length() == 1) {
1608            s = "0" + t + ":";
1609        }
1610        t = Integer.toString(minute);
1611        if (t.length() == 2) {
1612            s = s + t;
1613        } else if (t.length() == 1) {
1614            s = s + "0" + t;
1615        }
1616        if (s.length() != 5) {
1617            // input error
1618            s = "00:00";
1619        }
1620        return s;
1621    }
1622
1623    @Override
1624    public String getClassDescription() {
1625        return Bundle.getMessage("TitleLogixTable");        // NOI18N
1626    }
1627
1628    @Override
1629    protected String getClassName() {
1630        return LogixTableAction.class.getName();
1631    }
1632
1633    // ------------ Methods for Conditional References Window ------------
1634    /**
1635     * Builds the conditional references window when the Conditional Variable
1636     * References menu item is selected.
1637     * <p>
1638     * This is a stand-alone window that can be closed at any time.
1639     *
1640     * @since 4.7.4
1641     */
1642    void makeWhereUsedWindow() {
1643
1644        JmriJFrame referenceListFrame = new JmriJFrame(Bundle.getMessage("LabelRefTitle"), false, true);    // NOI18N
1645        Container contentPane = referenceListFrame.getContentPane();
1646        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
1647
1648        // build header information
1649        JPanel panel1 = new JPanel();
1650        panel1.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 5));
1651        panel1.add(new JLabel(Bundle.getMessage("LabelRefTarget")));    // NOI18N
1652        panel1.add(new JLabel(Bundle.getMessage("LabelRefSource")));    // NOI18N
1653        contentPane.add(panel1);
1654
1655        // Build the conditional references listing
1656        JScrollPane scrollPane = null;
1657        JTextArea textContent = buildWhereUsedListing();
1658        scrollPane = new JScrollPane(textContent);
1659        contentPane.add(scrollPane);
1660
1661        referenceListFrame.pack();
1662        referenceListFrame.setVisible(true);
1663    }
1664
1665    /**
1666     * Creates a component containing the conditional reference where used list.
1667     * The source is {@link jmri.ConditionalManager#getWhereUsedMap()}
1668     *
1669     * @return a TextArea, empty if reference is not used
1670     * @since 4.7.4
1671     */
1672    public JTextArea buildWhereUsedListing() {
1673        JTextArea condText = new javax.swing.JTextArea();
1674        condText.setText(null);
1675        HashMap<String, ArrayList<String>> whereUsed = InstanceManager.getDefault(ConditionalManager.class).getWhereUsedMap();
1676        SortedSet<String> targets = new TreeSet<>(whereUsed.keySet());
1677        targets.forEach((target) -> {
1678            condText.append("\n" + target + "\t" + getWhereUsedName(target) + "  \n");
1679            ArrayList<String> refNames = whereUsed.get(target);
1680            refNames.forEach((refName) -> {
1681                condText.append("\t\t" + refName + "\t" + getWhereUsedName(refName) + "  \n");
1682            });
1683        });
1684        condText.setCaretPosition(0);
1685        condText.setTabSize(2);
1686        condText.setEditable(false);
1687        return condText;
1688    }
1689
1690    String getWhereUsedName(String cName) {
1691        Conditional cond = _conditionalManager.getBySystemName(cName);
1692        if ( cond!=null){
1693            return cond.getUserName();
1694        }
1695        return "";
1696    }
1697
1698// ------------ Methods for Conditional Browser Window ------------
1699    /**
1700     * Respond to the Browse button pressed in Logix table.
1701     *
1702     * @param sName The selected Logix system name
1703     */
1704    void browserPressed(String sName) {
1705        makeBrowserWindow(sName);
1706    }
1707
1708    /**
1709     * Create and initialize the conditionals browser window.
1710     * @param lgxName Logix system name
1711     */
1712    void makeBrowserWindow(String lgxName) {
1713        Logix logix = _logixManager.getBySystemName(lgxName);
1714        if (logix == null) {
1715            return;
1716        }
1717            // Logix was found, create the window
1718        JmriJFrame condBrowserFrame = new JmriJFrame(Bundle.getMessage("BrowserTitle"), false, true);   // NOI18N
1719        condBrowserFrame.addHelpMenu("package.jmri.jmrit.beantable.LogixAddEdit", true);            // NOI18N
1720
1721        Container contentPane = condBrowserFrame.getContentPane();
1722        contentPane.setLayout(new BorderLayout());
1723
1724        // LOGIX header information
1725        JPanel topPanel = new JPanel();
1726        String tStr = Bundle.getMessage("BrowserLogix") + " " + logix.getSystemName() + "    " // NOI18N
1727                + logix.getUserName() + "    "
1728                + (Boolean.valueOf(logix.getEnabled())
1729                        ? Bundle.getMessage("BrowserEnabled") // NOI18N
1730                        : Bundle.getMessage("BrowserDisabled"));  // NOI18N
1731        topPanel.add(new JLabel(tStr));
1732        contentPane.add(topPanel, BorderLayout.NORTH);
1733
1734        // Build the conditionals listing
1735        JScrollPane scrollPane = null;
1736        JTextArea textContent = buildConditionalListing(logix);
1737        scrollPane = new JScrollPane(textContent);
1738        contentPane.add(scrollPane);
1739
1740        JPanel bottomPanel = new JPanel();
1741        bottomPanel.setLayout(new BorderLayout());
1742        JButton helpBrowse = new JButton(Bundle.getMessage("MenuHelp"));   // NOI18N
1743        bottomPanel.add(helpBrowse, BorderLayout.WEST);
1744        helpBrowse.addActionListener(new ActionListener() {
1745            @Override
1746            public void actionPerformed(ActionEvent e) {
1747                JmriJOptionPane.showMessageDialog(condBrowserFrame,
1748                        Bundle.getMessage("BrowserHelpText"),   // NOI18N
1749                        Bundle.getMessage("BrowserHelpTitle"),  // NOI18N
1750                        JmriJOptionPane.INFORMATION_MESSAGE);
1751            }
1752        });
1753        JButton saveBrowse = new JButton(Bundle.getMessage("BrowserSaveButton"));   // NOI18N
1754        saveBrowse.setToolTipText(Bundle.getMessage("BrowserSaveButtonHint"));      // NOI18N
1755        bottomPanel.add(saveBrowse, BorderLayout.EAST);
1756        saveBrowse.addActionListener(new SaveAction(lgxName));
1757        contentPane.add(bottomPanel, BorderLayout.SOUTH);
1758
1759        condBrowserFrame.pack();
1760        condBrowserFrame.setVisible(true);
1761    }  // makeBrowserWindow
1762
1763    class SaveAction implements ActionListener {
1764        String _lgxName;
1765        SaveAction(String lgxName) {
1766            _lgxName = lgxName;
1767        }
1768        @Override
1769        public void actionPerformed(ActionEvent e) {
1770            saveBrowserPressed(_lgxName);
1771        }
1772    }
1773
1774    /**
1775     * Save the Logix browser window content to a text file.
1776     * @param lgxName Logix system name
1777     */
1778    void saveBrowserPressed(String lgxName) {
1779        Logix logix = _logixManager.getBySystemName(lgxName);
1780        if (logix == null) {
1781            log.warn("Can't save browsed data, logix {} no longer exits", lgxName);
1782            return;
1783        }
1784        JFileChooser userFileChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath());
1785        userFileChooser.setApproveButtonText(Bundle.getMessage("BrowserSaveDialogApprove"));  // NOI18N
1786        userFileChooser.setDialogTitle(Bundle.getMessage("BrowserSaveDialogTitle"));  // NOI18N
1787        userFileChooser.rescanCurrentDirectory();
1788        // Default to logix system name.txt
1789        userFileChooser.setSelectedFile(new File(FileUtil.sanitizeFilename(logix.getSystemName()) + ".txt"));  // NOI18N
1790        int retVal = userFileChooser.showSaveDialog(null);
1791        if (retVal != JFileChooser.APPROVE_OPTION) {
1792            log.debug("Save browser content stopped, no file selected");  // NOI18N
1793            return;  // give up if no file selected or cancel pressed
1794        }
1795        File file = userFileChooser.getSelectedFile();
1796        log.debug("Save browser content to '{}'", file);  // NOI18N
1797
1798        if (file.exists()) {
1799            Object[] options = {Bundle.getMessage("BrowserSaveDuplicateReplace"),  // NOI18N
1800                    Bundle.getMessage("BrowserSaveDuplicateAppend"),  // NOI18N
1801                    Bundle.getMessage("ButtonCancel")};               // NOI18N
1802            int selectedOption = JmriJOptionPane.showOptionDialog(null,
1803                    Bundle.getMessage("BrowserSaveDuplicatePrompt", file.getName()), // NOI18N
1804                    Bundle.getMessage("BrowserSaveDuplicateTitle"),   // NOI18N
1805                    JmriJOptionPane.DEFAULT_OPTION,
1806                    JmriJOptionPane.WARNING_MESSAGE,
1807                    null, options, options[0]);
1808            if (selectedOption == 2 || selectedOption == -1) {
1809                log.debug("Save browser content stopped, file replace/append cancelled");  // NOI18N
1810                return;  // Cancel selected or dialog box closed
1811            }
1812            if (selectedOption == 0) {
1813                FileUtil.delete(file);  // Replace selected
1814            }
1815        }
1816
1817        // Create the file content
1818        String tStr = Bundle.getMessage("BrowserLogix") + " " + logix.getSystemName() + "    "  // NOI18N
1819                + logix.getUserName() + "    "
1820                + (Boolean.valueOf(logix.getEnabled())
1821                        ? Bundle.getMessage("BrowserEnabled")    // NOI18N
1822                        : Bundle.getMessage("BrowserDisabled"));  // NOI18N
1823        JTextArea textContent = buildConditionalListing(logix);
1824        try {
1825            // ADD Logix Header inforation first
1826            FileUtil.appendTextToFile(file, tStr);
1827            FileUtil.appendTextToFile(file, textContent.getText());
1828        } catch (IOException e) {
1829            log.error("Unable to write browser content to '{}'", file, e);  // NOI18N
1830        }
1831    }
1832
1833    /**
1834     * Builds a Component representing the current conditionals for the selected
1835     * Logix statement.
1836     *
1837     *@param logix browsing Logix
1838     * @return a TextArea listing existing conditionals; will be empty if there
1839     *         are none
1840     */
1841    JTextArea buildConditionalListing(Logix logix) {
1842        String showSystemName,
1843                showCondName,
1844                condName,
1845                operand,
1846                tStr;
1847
1848        List<ConditionalVariable> variableList;
1849        List<ConditionalAction> actionList;
1850        ConditionalVariable variable;
1851        ConditionalAction action;
1852        String _antecedent = null;
1853
1854        JTextArea condText = new javax.swing.JTextArea();
1855        condText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
1856        condText.setText(null);
1857        int numConditionals = logix.getNumConditionals();
1858        for (int rx = 0; rx < numConditionals; rx++) {
1859            conditionalRowNumber = rx;
1860            Conditional curConditional = _conditionalManager.getBySystemName(logix.getConditionalByNumberOrder(rx));
1861            if (curConditional==null){
1862                continue;
1863            }
1864            variableList = curConditional.getCopyOfStateVariables();
1865            actionList = curConditional.getCopyOfActions();
1866
1867            showCondName = curConditional.getUserName();
1868            if (showCondName == null) {
1869                showCondName = "";
1870            }
1871            showSystemName = curConditional.getSystemName();
1872
1873            // If no user name for a conditional, create one using C + row number
1874            if (showCondName.equals("")) {
1875                showCondName = "C" + (rx + 1);
1876            }
1877            condText.append("\n  " + showSystemName + "  " + showCondName + "   \n");
1878            if (curConditional.getLogicType() == Conditional.AntecedentOperator.MIXED) {
1879                _antecedent = curConditional.getAntecedentExpression();
1880                String antecedent = ConditionalEditBase.translateAntecedent(_antecedent, false);
1881                condText.append("   " + Bundle.getMessage("LogixAntecedent") + " " + antecedent + "  \n");   // NOI18N
1882            }
1883
1884            for (int i = 0; i < variableList.size(); i++) {
1885                variable = variableList.get(i);
1886                String varTrigger = (variable.doTriggerActions())
1887                        ? "[x]" // NOI18N
1888                        : "[ ]";
1889                tStr = "    " + varTrigger + " ";
1890                tStr = tStr + " R" + (i + 1) + (i > 8 ? " " : "  ");  // Makes {Rx}bb or {Rxx}b
1891                condText.append(tStr);
1892
1893                operand = variable.getOpernString();
1894                if (i == 0) { // add the IF to the first conditional
1895                    condText.append(Bundle.getMessage("BrowserIF") + " " + operand + " ");    // NOI18N
1896                } else {
1897                    condText.append("  " + operand + " ");
1898                }
1899                if (variable.isNegated()) {
1900                    condText.append(Bundle.getMessage("LogicNOT") + " ");     // NOI18N
1901                }
1902                condText.append(variable.toString() + "   \n");
1903            } // for _variableList
1904
1905            if (actionList.size() > 0) {
1906                condText.append("             " + Bundle.getMessage("BrowserTHEN") + "   \n");  // NOI18N
1907                boolean triggerType = curConditional.getTriggerOnChange();
1908                for (int i = 0; i < actionList.size(); i++) {
1909                    action = actionList.get(i);
1910                    condName = action.description(triggerType);
1911                    condText.append("               " + condName + "   \n");
1912                }  // for _actionList
1913            } else {
1914                condText.append("             " + Bundle.getMessage("BrowserNoAction") + "   \n\n");    // NOI18N
1915            }
1916        } // for numConditionals
1917
1918        condText.setCaretPosition(0);
1919        condText.setTabSize(4);
1920        condText.setEditable(false);
1921        return condText;
1922    }  // buildConditionalListing
1923
1924    private final static Logger log = LoggerFactory.getLogger(LogixTableAction.class);
1925
1926}