001package jmri.jmrit.beantable;
002
003import java.awt.event.*;
004import java.util.*;
005import java.util.List;
006
007import javax.annotation.Nonnull;
008import javax.swing.*;
009import javax.swing.table.TableColumn;
010
011import jmri.InstanceManager;
012import jmri.Manager;
013import jmri.NamedBean;
014import jmri.NamedBean.BadSystemNameException;
015import jmri.NamedBean.BadUserNameException;
016import jmri.UserPreferencesManager;
017import jmri.jmrit.logixng.Base;
018import jmri.jmrit.logixng.tools.swing.AbstractLogixNGEditor;
019import jmri.jmrit.logixng.tools.swing.DeleteBean;
020import jmri.jmrit.logixng.tools.swing.LogixNGBrowseWindow;
021import jmri.util.JmriJFrame;
022import jmri.util.swing.JmriJOptionPane;
023
024/**
025 * Swing action to create and register a LogixNG Table.
026 * <p>
027 Also contains the panes to create, edit, and delete a LogixNG.
028 <p>
029 * Most of the text used in this GUI is in BeanTableBundle.properties, accessed
030 * via Bundle.getMessage().
031 *
032 * @author Dave Duchamp Copyright (C) 2007 (LogixTableAction)
033 * @author Pete Cressman Copyright (C) 2009, 2010, 2011 (LogixTableAction)
034 * @author Matthew Harris copyright (c) 2009 (LogixTableAction)
035 * @author Dave Sand copyright (c) 2017 (LogixTableAction)
036 * @author Daniel Bergqvist copyright (c) 2019 (AbstractLogixNGTableEditor)
037 * @author Dave Sand copyright (c) 2021 (AbstractLogixNGTableEditor)
038 *
039 * @param <E> the type of NamedBean supported by this model
040 */
041public abstract class AbstractLogixNGTableAction<E extends NamedBean> extends AbstractTableAction<E> {
042
043
044    private static final ResourceBundle rbx = ResourceBundle.getBundle("jmri.jmrit.logixng.LogixNGBundle");
045    private static final ResourceBundle rbx2 = ResourceBundle.getBundle("jmri.jmrit.logixng.tools.swing.LogixNGSwingBundle");
046
047    DeleteBean<E> deleteBean = new DeleteBean<>(getManager());
048
049    /**
050     * Create a AbstractLogixNGTableAction instance.
051     *
052     * @param s the Action title, not the title of the resulting frame. Perhaps
053     *          this should be changed?
054     */
055    public AbstractLogixNGTableAction(String s) {
056        super(s);
057        super.setEnabled(false);
058    }
059
060    protected abstract AbstractLogixNGEditor<E> getEditor(BeanTableDataModel<E> m, String sName);
061
062    protected boolean isEditSupported() {
063        return true;
064    }
065
066    @Nonnull
067    @Override
068    protected abstract Manager<E> getManager();
069
070    protected abstract void enableAll(boolean enable);
071
072    protected abstract void setEnabled(E bean, boolean enable);
073
074    protected abstract boolean isEnabled(E bean);
075
076    protected abstract E createBean(String userName);
077
078    protected abstract E createBean(String systemName, String userName);
079
080    protected abstract void deleteBean(E bean);
081
082    protected boolean browseMonoSpace() { return false; }
083
084    protected abstract String getBeanText(E bean, Base.PrintTreeSettings printTreeSettings);
085
086    protected abstract String getBrowserTitle();
087
088    protected abstract String getAddTitleKey();
089
090    protected abstract String getCreateButtonHintKey();
091
092    protected abstract void getListenerRefsIncludingChildren(E t, List<String> list);
093
094    protected abstract boolean hasChildren(E t);
095
096    // ------------ Methods for LogixNG Table Window ------------
097
098    /**
099     * Create the JTable DataModel, along with the changes (overrides of
100     * BeanTableDataModel) for the specific case of a LogixNG table.
101     */
102    @Override
103    protected void createModel() {
104        m = new TableModel();
105    }
106
107    /**
108     * Set title of NamedBean table.
109     */
110    @Override
111    protected void setTitle() {
112        f.setTitle(Bundle.getMessage("TitleLogixNGTable"));
113    }
114
115    /**
116     * Insert 2 table specific menus.
117     * <p>
118     * Accounts for the Window and Help menus, which are already added to the
119     * menu bar as part of the creation of the JFrame, by adding the new menus 2
120     * places earlier unless the table is part of the ListedTableFrame, which
121     * adds the Help menu later on.
122     *
123     * @param f the JFrame of this table
124     */
125    @Override
126    public void setMenuBar(BeanTableFrame<E> f) {
127        JMenu menu = new JMenu(Bundle.getMessage("MenuOptions"));  // NOI18N
128        menu.setMnemonic(KeyEvent.VK_O);
129        JMenuBar menuBar = f.getJMenuBar();
130        int pos = menuBar.getMenuCount() - 1;  // count the number of menus to insert the TableMenus before 'Window' and 'Help'
131        int offset = 1;
132        log.debug("setMenuBar number of menu items = {}", pos);  // NOI18N
133        for (int i = 0; i <= pos; i++) {
134            if (menuBar.getComponent(i) instanceof JMenu) {
135                if (((JMenu) menuBar.getComponent(i)).getText().equals(Bundle.getMessage("MenuHelp"))) {  // NOI18N
136                    offset = -1;  // correct for use as part of ListedTableAction where the Help Menu is not yet present
137                }
138            }
139        }
140
141        // Do not include this menu for Module or Table tables
142        if (this instanceof LogixNGTableAction) {
143            JMenuItem r = new JMenuItem(Bundle.getMessage("EnableAllLogixNGs"));  // NOI18N
144            r.addActionListener((ActionEvent e) -> {
145                enableAll(true);
146            });
147            menu.add(r);
148
149            r = new JMenuItem(Bundle.getMessage("DisableAllLogixNGs"));  // NOI18N
150            r.addActionListener((ActionEvent e) -> {
151                enableAll(false);
152            });
153            menu.add(r);
154
155            menuBar.add(menu, pos + offset);
156            offset++;
157        }
158
159        menu = new JMenu(Bundle.getMessage("MenuTools"));  // NOI18N
160        menu.setMnemonic(KeyEvent.VK_T);
161
162        JMenuItem item = new JMenuItem(rbx2.getString("MenuOpenClipboard"));  // NOI18N
163        item.addActionListener((ActionEvent e) -> {
164            jmri.jmrit.logixng.tools.swing.TreeEditor.openClipboard();
165        });
166        menu.add(item);
167
168        item = new JMenuItem(Bundle.getMessage("OpenPickListTables"));  // NOI18N
169        item.addActionListener((ActionEvent e) -> {
170            openPickListTable();
171        });
172        menu.add(item);
173
174        menuBar.add(menu, pos + offset);  // add this menu to the right of the previous
175    }
176
177    /**
178     * Open a new Pick List to drag Actions from to form NamedBean.
179     */
180    private void openPickListTable() {
181        if (_pickTables == null) {
182            _pickTables = new jmri.jmrit.picker.PickFrame(Bundle.getMessage("TitlePickList"));  // NOI18N
183        } else {
184            _pickTables.setVisible(true);
185        }
186        _pickTables.toFront();
187    }
188
189    @Override
190    protected String helpTarget() {
191        return "package.jmri.jmrit.beantable.LogixNGTable";  // NOI18N
192    }
193
194    // ------------ variable definitions ------------
195
196    protected AbstractLogixNGEditor<E> _editor = null;
197
198    boolean _showReminder = false;
199    private boolean _checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled();
200    jmri.jmrit.picker.PickFrame _pickTables;
201
202    // Current focus variables
203    protected E _curNamedBean = null;
204    int conditionalRowNumber = 0;
205
206    // Add E Variables
207    JmriJFrame addLogixNGFrame = null;
208    JTextField _systemName = new JTextField(20);
209    JTextField _addUserName = new JTextField(20);
210    JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName"));   // NOI18N
211    JLabel _sysNameLabel = new JLabel(rbx.getString("BeanNameLogixNG") + " " + Bundle.getMessage("ColumnSystemName") + ":");  // NOI18N
212    JLabel _userNameLabel = new JLabel(rbx.getString("BeanNameLogixNG") + " " + Bundle.getMessage("ColumnUserName") + ":");   // NOI18N
213    String systemNameAuto = this.getClassName() + ".AutoSystemName";       // NOI18N
214    JButton create;
215
216    // Edit E Variables
217    private boolean _inEditMode = false;
218    private boolean _inCopyMode = false;
219
220    // ------------ Methods for Add bean Window ------------
221
222    /**
223     * Respond to the Add button in bean table Creates and/or initialize
224     * the Add bean pane.
225     *
226     * @param e The event heard
227     */
228    @Override
229    protected void addPressed(ActionEvent e) {
230        // possible change
231        if (!checkFlags(null)) {
232            return;
233        }
234        _showReminder = true;
235        // make an Add bean Frame
236        if (addLogixNGFrame == null) {
237            String titleKey = getAddTitleKey();
238            String buttonHintKey = getCreateButtonHintKey();
239            JPanel panel5 = makeAddFrame(titleKey, "Add");  // NOI18N
240            // Create bean
241            create = new JButton(Bundle.getMessage("ButtonCreate"));  // NOI18N
242            panel5.add(create);
243            create.addActionListener(this::createPressed);
244            create.setToolTipText(Bundle.getMessage(buttonHintKey));  // NOI18N
245        }
246        addLogixNGFrame.pack();
247        addLogixNGFrame.setVisible(true);
248        _autoSystemName.setSelected(false);
249        InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
250            _autoSystemName.setSelected(prefMgr.getCheckboxPreferenceState(systemNameAuto, true));
251        });
252    }
253
254    protected abstract JPanel makeAddFrame(String titleId, String startMessageId);
255
256    /**
257     * Enable/disable fields for data entry when user selects to have system
258     * name automatically generated.
259     */
260    void autoSystemName() {
261        if (_autoSystemName.isSelected()) {
262            _systemName.setEnabled(false);
263            _sysNameLabel.setEnabled(false);
264        } else {
265            _systemName.setEnabled(true);
266            _sysNameLabel.setEnabled(true);
267        }
268    }
269
270    /**
271     * Respond to the Cancel button in Add bean window.
272     * <p>
273     * Note: Also get there if the user closes the Add bean window.
274     *
275     * @param e The event heard
276     */
277    void cancelAddPressed(ActionEvent e) {
278        addLogixNGFrame.setVisible(false);
279        addLogixNGFrame.dispose();
280        addLogixNGFrame = null;
281        _inCopyMode = false;
282        if (f != null) {
283            f.setVisible(true);
284        }
285    }
286
287    /**
288     * Respond to the Copy bean button in Add bean window.
289     * <p>
290     * Provides a pane to set new properties of the copy.
291     *
292     * @param sName system name of bean to be copied
293     */
294    void copyPressed(String sName) {
295        if (!checkFlags(sName)) {
296            return;
297        }
298
299        Runnable t = new Runnable() {
300            @Override
301            public void run() {
302//                JmriJOptionPane.showMessageDialog(null, "Copy is not implemented yet.", "Error", JmriJOptionPane.ERROR_MESSAGE);
303
304                JPanel panel5 = makeAddFrame("TitleCopyLogixNG", "Copy");    // NOI18N
305                // Create bean
306                JButton create = new JButton(Bundle.getMessage("ButtonCopy"));  // NOI18N
307                panel5.add(create);
308                create.addActionListener((ActionEvent e) -> {
309                    copyBeanPressed(e);
310                });
311                addLogixNGFrame.pack();
312                addLogixNGFrame.setVisible(true);
313                _autoSystemName.setSelected(false);
314                InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
315                    _autoSystemName.setSelected(prefMgr.getCheckboxPreferenceState(systemNameAuto, true));
316                });
317
318                _inCopyMode = false;
319            }
320        };
321        log.debug("copyPressed started for {}", sName);  // NOI18N
322        javax.swing.SwingUtilities.invokeLater(t);
323        _inCopyMode = true;
324        _logixNGSysName = sName;
325    }
326
327    String _logixNGSysName;
328
329    protected void copyBean(@Nonnull E sourceBean, @Nonnull E targetBean) {
330        throw new UnsupportedOperationException("Not implemented");
331    }
332
333    protected boolean isCopyBeanSupported() {
334        return false;
335    }
336
337    protected boolean isExecuteSupported() {
338        return false;
339    }
340
341    protected void execute(@Nonnull E bean) {
342        throw new UnsupportedOperationException("Not implemented");
343    }
344
345    /**
346     * Copy the bean as configured in the Copy set up pane.
347     *
348     * @param e the event heard
349     */
350    private void copyBeanPressed(ActionEvent e) {
351
352        String uName = _addUserName.getText().trim();
353        if (uName.length() == 0) {
354            uName = null;
355        }
356        E targetBean;
357        if (_autoSystemName.isSelected()) {
358            if (!checkLogixNGUserName(uName)) {
359                return;
360            }
361            targetBean = createBean(uName);
362        } else {
363            if (!checkLogixNGSysName()) {
364                return;
365            }
366            String sName = _systemName.getText().trim();
367            // check if a bean with this name already exists
368            boolean createLogix = true;
369            targetBean = getManager().getBySystemName(sName);
370            if (targetBean != null) {
371                int result = JmriJOptionPane.showConfirmDialog(f,
372                        Bundle.getMessage("ConfirmLogixDuplicate", sName, _logixNGSysName), // NOI18N
373                        Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,    // NOI18N
374                        JmriJOptionPane.QUESTION_MESSAGE);
375                if (JmriJOptionPane.NO_OPTION == result) {
376                    return;
377                }
378                createLogix = false;
379                String userName = targetBean.getUserName();
380                if (userName != null && userName.length() > 0) {
381                    _addUserName.setText(userName);
382                    uName = userName;
383                }
384            } else if (!checkLogixNGUserName(uName)) {
385                return;
386            }
387            if (createLogix) {
388                // Create the new LogixNG
389                targetBean = createBean(sName, uName);
390                if (targetBean == null) {
391                    // should never get here unless there is an assignment conflict
392                    log.error("Failure to create LogixNG with System Name: {}", sName);  // NOI18N
393                    return;
394                }
395            } else if (targetBean == null) {
396                log.error("Error targetLogix is null!");  // NOI18N
397                return;
398            } else {
399                targetBean.setUserName(uName);
400            }
401        }
402        E sourceBean = getManager().getBySystemName(_logixNGSysName);
403        if (sourceBean != null) copyBean(sourceBean, targetBean);
404        else log.error("Error targetLogix is null!");  // NOI18N
405        cancelAddPressed(null);
406    }
407
408    /**
409     * Check and warn if a string is already in use as the user name of a LogixNG.
410     *
411     * @param uName the suggested name
412     * @return true if not in use
413     */
414    boolean checkLogixNGUserName(String uName) {
415        // check if a bean with the same user name exists
416        if (uName != null && uName.trim().length() > 0) {
417            E x = getManager().getByUserName(uName);
418            if (x != null) {
419                // A bean with this user name already exists
420                JmriJOptionPane.showMessageDialog(addLogixNGFrame,
421                        Bundle.getMessage("LogixNGError3"), Bundle.getMessage("ErrorTitle"), // NOI18N
422                        JmriJOptionPane.ERROR_MESSAGE);
423                return false;
424            }
425        }
426        return true;
427    }
428
429    /**
430     * Check validity of various LogixNG system names.
431     * <p>
432     * Fixes name if it doesn't start with the appropriate prefix or the $ for alpha suffixes
433     *
434     * @return false if the name fails the NameValidity check
435     */
436    boolean checkLogixNGSysName() {
437        if (_autoSystemName.isSelected()) {
438            return true;
439        }
440
441        var sName = _systemName.getText().trim();
442        var prefix = getManager().getSubSystemNamePrefix();
443
444        if (!sName.isEmpty() && !sName.startsWith(prefix)) {
445            var isNumber = sName.matches("^\\d+$");
446            var hasDollar = sName.startsWith("$");
447
448            var newName = new StringBuilder(prefix);
449            if (!isNumber && !hasDollar) {
450                newName.append("$");
451            }
452            newName.append(sName);
453            sName = newName.toString();
454        }
455
456        if (getManager().validSystemNameFormat(sName) != jmri.Manager.NameValidity.VALID) {
457            JmriJOptionPane.showMessageDialog(null,
458                    Bundle.getMessage("LogixNGError8", sName), Bundle.getMessage("ErrorTitle"), // NOI18N
459                    JmriJOptionPane.ERROR_MESSAGE);
460            return false;
461        }
462
463        _systemName.setText(sName);
464        return true;
465    }
466
467    /**
468     * Check if another bean editing session is currently open or no system
469     * name is provided.
470     *
471     * @param sName system name of bean to be copied
472     * @return true if a new session may be started
473     */
474    boolean checkFlags(String sName) {
475        if (_inEditMode) {
476            // Already editing a bean, ask for completion of that edit
477            JmriJOptionPane.showMessageDialog(null,
478                    Bundle.getMessage("LogixNGError32", _curNamedBean.getSystemName()),
479                    Bundle.getMessage("ErrorTitle"),
480                    JmriJOptionPane.ERROR_MESSAGE);
481            if (_editor != null) {
482                _editor.bringToFront();
483            }
484            return false;
485        }
486
487        if (_inCopyMode) {
488            // Already editing a bean, ask for completion of that edit
489            JmriJOptionPane.showMessageDialog(null,
490                    Bundle.getMessage("LogixNGError31", _logixNGSysName),
491                    Bundle.getMessage("ErrorTitle"), // NOI18N
492                    JmriJOptionPane.ERROR_MESSAGE);
493            return false;
494        }
495
496        if (sName != null) {
497            // check if a bean with this name exists
498            E x = getManager().getBySystemName(sName);
499            if (x == null) {
500                // bean does not exist, so cannot be edited
501                log.error("No bean with system name: {}", sName);
502                JmriJOptionPane.showMessageDialog(null,
503                        Bundle.getMessage("LogixNGError5"),
504                        Bundle.getMessage("ErrorTitle"), // NOI18N
505                        JmriJOptionPane.ERROR_MESSAGE);
506                return false;
507            }
508        }
509        return true;
510    }
511
512    /**
513     * Respond to the Create bean button in Add bean window.
514     *
515     * @param e The event heard
516     */
517    void createPressed(ActionEvent e) {
518        // possible change
519        _showReminder = true;
520        String sName;
521        String uName = _addUserName.getText().trim();
522        if (uName.length() == 0) {
523            uName = null;
524        }
525        if (_autoSystemName.isSelected()) {
526            if (!checkLogixNGUserName(uName)) {
527                return;
528            }
529            try {
530                _curNamedBean = createBean(uName);
531            } catch (BadSystemNameException | BadUserNameException ex) {
532                JmriJOptionPane.showMessageDialog(addLogixNGFrame, ex.getLocalizedMessage(),
533                        Bundle.getMessage("ErrorTitle"), // NOI18N
534                        JmriJOptionPane.ERROR_MESSAGE);
535                return;
536            }
537            if (_curNamedBean == null) {
538                log.error("Failure to create bean with System Name: {}", "none");  // NOI18N
539                return;
540            }
541            sName = _curNamedBean.getSystemName();
542        } else {
543            if (!checkLogixNGSysName()) {
544                return;
545            }
546            // Get validated system name
547            sName = _systemName.getText();
548            // check if a bean with this name already exists
549            E x;
550            try {
551                x = getManager().getBySystemName(sName);
552            } catch (Exception ex) {
553                // user input no good
554                handleCreateException(sName);
555                return;  // without creating
556            }
557            if (x != null) {
558                // bean already exists
559                JmriJOptionPane.showMessageDialog(addLogixNGFrame, Bundle.getMessage("LogixNGError1"),
560                        Bundle.getMessage("ErrorTitle"), // NOI18N
561                        JmriJOptionPane.ERROR_MESSAGE);
562                return;
563            }
564            if (!checkLogixNGUserName(uName)) {
565                return;
566            }
567            // Create the new bean
568            _curNamedBean = createBean(sName, uName);
569            if (_curNamedBean == null) {
570                // should never get here unless there is an assignment conflict
571                log.error("Failure to create bean with System Name: {}", sName);  // NOI18N
572                return;
573            }
574        }
575        cancelAddPressed(null);
576        // create the Edit bean Window
577        editPressed(sName);
578        InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
579            prefMgr.setCheckboxPreferenceState(systemNameAuto, _autoSystemName.isSelected());
580        });
581    }
582
583    void handleCreateException(String sysName) {
584        JmriJOptionPane.showMessageDialog(addLogixNGFrame,
585                Bundle.getMessage("ErrorLogixAddFailed", sysName), // NOI18N
586                Bundle.getMessage("ErrorTitle"), // NOI18N
587                JmriJOptionPane.ERROR_MESSAGE);
588    }
589
590    // ------------ Methods for Edit bean Pane ------------
591
592    /**
593     * Respond to the Edit button pressed in LogixNG table.
594     *
595     * @param sName system name of LogixNG to be edited
596     */
597    void editPressed(String sName) {
598        _curNamedBean = getManager().getBySystemName(sName);
599        if (!checkFlags(sName)) {
600            return;
601        }
602
603        // Create a new bean edit view, add the listener.
604        _editor = getEditor(m, sName);
605
606        if (_editor == null) return;    // Editor not implemented yet for LogixNG Tables
607
608        _inEditMode = true;
609
610        _editor.addEditorEventListener((data) -> {
611            String lgxName = sName;
612            data.forEach((key, value) -> {
613                if (key.equals("Finish")) {                  // NOI18N
614                    _editor = null;
615                    _inEditMode = false;
616                    f.setVisible(true);
617                } else if (key.equals("Delete")) {           // NOI18N
618                    _inEditMode = false;
619                    deletePressed(value);
620                } else if (key.equals("chgUname")) {         // NOI18N
621                    E x = getManager().getBySystemName(lgxName);
622                    if (x == null) {
623                        log.error("Found no logixNG for name {} when changing user name (2)", lgxName);
624                        return;
625                    }
626                    x.setUserName(value);
627                    m.fireTableDataChanged();
628                }
629            });
630        });
631    }
632
633    /**
634     * Display reminder to save.
635     */
636    void showSaveReminder() {
637        if (_showReminder && !_checkEnabled) {
638            if (InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class) != null) {
639                InstanceManager.getDefault(jmri.UserPreferencesManager.class).
640                        showInfoMessage(Bundle.getMessage("ReminderTitle"), Bundle.getMessage("ReminderSaveString", Bundle.getMessage("MenuItemLogixNGTable")), // NOI18N
641                                getClassName(),
642                                "remindSaveLogix");  // NOI18N
643            }
644        }
645    }
646
647    @Override
648    public void setMessagePreferencesDetails() {
649        HashMap<Integer, String> options = new HashMap<>(3);
650        options.put(0x00, Bundle.getMessage("DeleteAsk"));      // NOI18N
651        options.put(0x01, Bundle.getMessage("DeleteNever"));    // NOI18N
652        options.put(0x02, Bundle.getMessage("DeleteAlways"));   // NOI18N
653        jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class).setMessageItemDetails(getClassName(), "delete", Bundle.getMessage("DeleteLogixNG"), options, 0x00);  // NOI18N
654        jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class).setPreferenceItemDetails(getClassName(), "remindSaveLogixNG", Bundle.getMessage("HideSaveReminder"));  // NOI18N
655        super.setMessagePreferencesDetails();
656    }
657
658    /**
659     * Respond to the Delete combo selection bean window delete request.
660     *
661     * @param sName system name of bean to be deleted
662     */
663    void deletePressed(String sName) {
664        if (!checkFlags(sName)) {
665            return;
666        }
667
668        final E x = getManager().getBySystemName(sName);
669
670        if (x == null) return;  // This should never happen
671
672        deleteBean.delete(x, hasChildren(x), (t)->{deleteBean(t);},
673                (t,list)->{getListenerRefsIncludingChildren(t,list);},
674                getClassName());
675    }
676
677    /**
678     * Respond to the Execute combo selection bean window execute request.
679     *
680     * @param sName system name of bean to be deleted
681     */
682    void executePressed(String sName) {
683        final E x = getManager().getBySystemName(sName);
684
685        if (x == null) return;  // This should never happen
686
687        execute(x);
688    }
689
690    @Override
691    public String getClassDescription() {
692        return Bundle.getMessage("TitleLogixNGTable");        // NOI18N
693    }
694
695    @Override
696    protected String getClassName() {
697        // The class that is returned must have a default constructor,
698        // a constructor with no parameters.
699        return jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName();
700    }
701
702// ------------ Methods for Conditional Browser Window ------------
703    /**
704     * Respond to the Browse button pressed in bean table.
705     *
706     * @param sName The selected bean system name
707     */
708    void browserPressed(String sName) {
709        // bean was found, create the window
710        _curNamedBean = getManager().getBySystemName(sName);
711
712        if (_curNamedBean == null) {
713            // This should never happen but SpotBugs complains about it.
714            throw new RuntimeException("_curNamedBean is null");
715        }
716
717        String title = Bundle.getMessage("BrowserLogixNG") + " " + _curNamedBean.getSystemName() + "    " // NOI18N
718                + _curNamedBean.getUserName() + "    "
719                + (isEnabled(_curNamedBean)
720                        ? Bundle.getMessage("BrowserEnabled") // NOI18N
721                        : Bundle.getMessage("BrowserDisabled"));  // NOI18N
722
723        LogixNGBrowseWindow browseWindow =
724                new LogixNGBrowseWindow(Bundle.getMessage("LogixNG_Browse_Title"));
725        browseWindow.getPrintTreeSettings();
726        boolean showSettingsPanel = this instanceof LogixNGTableAction || this instanceof LogixNGModuleTableAction;
727        browseWindow.makeBrowserWindow(browseMonoSpace(), showSettingsPanel, title, _curNamedBean.getSystemName(),
728                (printTreeSettings) -> {
729                        return AbstractLogixNGTableAction.this.getBeanText(_curNamedBean, printTreeSettings);
730                });
731    }
732
733
734
735    protected class TableModel extends BeanTableDataModel<E> {
736
737        // overlay the state column with the edit column
738        static public final int ENABLECOL = VALUECOL;
739        static public final int EDITCOL = DELETECOL;
740        protected String enabledString = Bundle.getMessage("ColumnHeadEnabled");  // NOI18N
741
742        @Override
743        public String getColumnName(int col) {
744            if (col == EDITCOL) {
745                return Bundle.getMessage("ColumnHeadMenu");     // This makes it easier to test the table
746            }
747            if (col == ENABLECOL) {
748                return enabledString;
749            }
750            return super.getColumnName(col);
751        }
752
753        @Override
754        public Class<?> getColumnClass(int col) {
755            if (col == EDITCOL) {
756                return String.class;
757            }
758            if (col == ENABLECOL) {
759                return Boolean.class;
760            }
761            return super.getColumnClass(col);
762        }
763
764        @Override
765        public int getPreferredWidth(int col) {
766            // override default value for SystemName and UserName columns
767            if (col == SYSNAMECOL) {
768                return new JTextField(12).getPreferredSize().width;
769            }
770            if (col == USERNAMECOL) {
771                return new JTextField(17).getPreferredSize().width;
772            }
773            if (col == EDITCOL) {
774                return new JTextField(12).getPreferredSize().width;
775            }
776            if (col == ENABLECOL) {
777                return new JTextField(5).getPreferredSize().width;
778            }
779            return super.getPreferredWidth(col);
780        }
781
782        @Override
783        public boolean isCellEditable(int row, int col) {
784            if (col == EDITCOL) {
785                return true;
786            }
787            if (col == ENABLECOL) {
788                return true;
789            }
790            return super.isCellEditable(row, col);
791        }
792
793        @SuppressWarnings("unchecked")  // Unchecked cast from Object to E
794        @Override
795        public Object getValueAt(int row, int col) {
796            if (col == EDITCOL) {
797                return Bundle.getMessage("ButtonSelect");  // NOI18N
798            } else if (col == ENABLECOL) {
799                E x = (E) getValueAt(row, SYSNAMECOL);
800                if (x == null) {
801                    return null;
802                }
803                return isEnabled(x);
804            } else {
805                return super.getValueAt(row, col);
806            }
807        }
808
809        @SuppressWarnings("unchecked")  // Unchecked cast from Object to E
810        @Override
811        public void setValueAt(Object value, int row, int col) {
812            if (col == EDITCOL) {
813                // set up to edit
814                String sName = ((NamedBean) getValueAt(row, SYSNAMECOL)).getSystemName();
815                if (Bundle.getMessage("ButtonEdit").equals(value)) {  // NOI18N
816                    editPressed(sName);
817
818                } else if (Bundle.getMessage("BrowserButton").equals(value)) {  // NOI18N
819                    conditionalRowNumber = row;
820                    browserPressed(sName);
821
822                } else if (Bundle.getMessage("ButtonCopy").equals(value)) {  // NOI18N
823                    copyPressed(sName);
824
825                } else if (Bundle.getMessage("ButtonDelete").equals(value)) {  // NOI18N
826                    deletePressed(sName);
827
828                } else if (Bundle.getMessage("LogixNG_ButtonExecute").equals(value)) {  // NOI18N
829                    executePressed(sName);
830                }
831            } else if (col == ENABLECOL) {
832                // alternate
833                E x = (E) getValueAt(row, SYSNAMECOL);
834                boolean v = isEnabled(x);
835                setEnabled(x, !v);
836            } else {
837                super.setValueAt(value, row, col);
838            }
839        }
840
841        /**
842         * Delete the bean after all the checking has been done.
843         * <p>
844         * Deletes the NamedBean.
845         *
846         * @param bean of the NamedBean to delete
847         */
848        @Override
849        protected void doDelete(E bean) {
850            // delete the LogixNG
851            AbstractLogixNGTableAction.this.deleteBean(bean);
852        }
853
854        @Override
855        protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
856            if (e.getPropertyName().equals(enabledString)) {
857                return true;
858            }
859            return super.matchPropertyName(e);
860        }
861
862        @Override
863        public Manager<E> getManager() {
864            return AbstractLogixNGTableAction.this.getManager();
865        }
866
867        @Override
868        public E getBySystemName(String name) {
869            return AbstractLogixNGTableAction.this.getManager().getBySystemName(name);
870        }
871
872        @Override
873        public E getByUserName(String name) {
874            return AbstractLogixNGTableAction.this.getManager().getByUserName(name);
875        }
876
877        @Override
878        protected String getMasterClassName() {
879            return getClassName();
880        }
881
882        @Override
883        public void configureTable(JTable table) {
884            table.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer());
885            table.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
886            table.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
887            if (!(getManager() instanceof jmri.jmrit.logixng.LogixNG_Manager)) {
888                table.getColumnModel().getColumn(2).setMinWidth(0);
889                table.getColumnModel().getColumn(2).setMaxWidth(0);
890            }
891            super.configureTable(table);
892        }
893
894        /**
895         * Replace delete button with comboBox to edit/delete/copy/select NamedBean.
896         *
897         * @param table name of the NamedBean JTable holding the column
898         */
899        @Override
900        protected void configDeleteColumn(JTable table) {
901            JComboBox<String> editCombo = new JComboBox<>();
902            editCombo.addItem(Bundle.getMessage("ButtonSelect"));  // NOI18N
903            if (isEditSupported()) editCombo.addItem(Bundle.getMessage("ButtonEdit"));  // NOI18N
904            editCombo.addItem(Bundle.getMessage("BrowserButton"));  // NOI18N
905            if (isCopyBeanSupported()) editCombo.addItem(Bundle.getMessage("ButtonCopy"));  // NOI18N
906            editCombo.addItem(Bundle.getMessage("ButtonDelete"));  // NOI18N
907            if (isExecuteSupported()) editCombo.addItem(Bundle.getMessage("LogixNG_ButtonExecute"));  // NOI18N
908            TableColumn col = table.getColumnModel().getColumn(BeanTableDataModel.DELETECOL);
909            col.setCellEditor(new DefaultCellEditor(editCombo));
910        }
911
912        // Not needed - here for interface compatibility
913        @Override
914        public void clickOn(NamedBean t) {
915        }
916
917        @Override
918        public String getValue(String s) {
919            return "";
920        }
921
922        @Override
923        protected String getBeanType() {
924//                 return Bundle.getMessage("BeanNameLogix");  // NOI18N
925            return rbx.getString("BeanNameLogixNG");  // NOI18N
926        }
927    }
928
929
930    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractLogixNGTableAction.class);
931
932}