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    jmri.jmrit.picker.PickFrame _pickTables;
200
201    // Current focus variables
202    protected E _curNamedBean = null;
203    int conditionalRowNumber = 0;
204
205    // Add E Variables
206    JmriJFrame addLogixNGFrame = null;
207    JTextField _systemName = new JTextField(20);
208    JTextField _addUserName = new JTextField(20);
209    JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName"));   // NOI18N
210    JLabel _sysNameLabel = new JLabel(rbx.getString("BeanNameLogixNG") + " " + Bundle.getMessage("ColumnSystemName") + ":");  // NOI18N
211    JLabel _userNameLabel = new JLabel(rbx.getString("BeanNameLogixNG") + " " + Bundle.getMessage("ColumnUserName") + ":");   // NOI18N
212    String systemNameAuto = this.getClassName() + ".AutoSystemName";       // NOI18N
213    JButton create;
214
215    // Edit E Variables
216    private boolean _inEditMode = false;
217    private boolean _inCopyMode = false;
218
219    // ------------ Methods for Add bean Window ------------
220
221    /**
222     * Respond to the Add button in bean table Creates and/or initialize
223     * the Add bean pane.
224     *
225     * @param e The event heard
226     */
227    @Override
228    protected void addPressed(ActionEvent e) {
229        // possible change
230        if (!checkFlags(null)) {
231            return;
232        }
233        _showReminder = true;
234        // make an Add bean Frame
235        if (addLogixNGFrame == null) {
236            String titleKey = getAddTitleKey();
237            String buttonHintKey = getCreateButtonHintKey();
238            JPanel panel5 = makeAddFrame(titleKey, "Add");  // NOI18N
239            // Create bean
240            create = new JButton(Bundle.getMessage("ButtonCreate"));  // NOI18N
241            panel5.add(create);
242            create.addActionListener(this::createPressed);
243            create.setToolTipText(Bundle.getMessage(buttonHintKey));  // NOI18N
244        }
245        addLogixNGFrame.pack();
246        addLogixNGFrame.setVisible(true);
247        _autoSystemName.setSelected(false);
248        InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
249            _autoSystemName.setSelected(prefMgr.getCheckboxPreferenceState(systemNameAuto, true));
250        });
251    }
252
253    protected abstract JPanel makeAddFrame(String titleId, String startMessageId);
254
255    /**
256     * Enable/disable fields for data entry when user selects to have system
257     * name automatically generated.
258     */
259    void autoSystemName() {
260        if (_autoSystemName.isSelected()) {
261            _systemName.setEnabled(false);
262            _sysNameLabel.setEnabled(false);
263        } else {
264            _systemName.setEnabled(true);
265            _sysNameLabel.setEnabled(true);
266        }
267    }
268
269    /**
270     * Respond to the Cancel button in Add bean window.
271     * <p>
272     * Note: Also get there if the user closes the Add bean window.
273     *
274     * @param e The event heard
275     */
276    void cancelAddPressed(ActionEvent e) {
277        addLogixNGFrame.setVisible(false);
278        addLogixNGFrame.dispose();
279        addLogixNGFrame = null;
280        _inCopyMode = false;
281        if (f != null) {
282            f.setVisible(true);
283        }
284    }
285
286    /**
287     * Respond to the Copy bean button in Add bean window.
288     * <p>
289     * Provides a pane to set new properties of the copy.
290     *
291     * @param sName system name of bean to be copied
292     */
293    void copyPressed(String sName) {
294        if (!checkFlags(sName)) {
295            return;
296        }
297
298        Runnable t = new Runnable() {
299            @Override
300            public void run() {
301//                JmriJOptionPane.showMessageDialog(null, "Copy is not implemented yet.", "Error", JmriJOptionPane.ERROR_MESSAGE);
302
303                JPanel panel5 = makeAddFrame("TitleCopyLogixNG", "Copy");    // NOI18N
304                // Create bean
305                JButton create = new JButton(Bundle.getMessage("ButtonCopy"));  // NOI18N
306                panel5.add(create);
307                create.addActionListener((ActionEvent e) -> {
308                    copyBeanPressed(e);
309                });
310                addLogixNGFrame.pack();
311                addLogixNGFrame.setVisible(true);
312                _autoSystemName.setSelected(false);
313                InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
314                    _autoSystemName.setSelected(prefMgr.getCheckboxPreferenceState(systemNameAuto, true));
315                });
316
317                _inCopyMode = false;
318            }
319        };
320        log.debug("copyPressed started for {}", sName);  // NOI18N
321        javax.swing.SwingUtilities.invokeLater(t);
322        _inCopyMode = true;
323        _logixNGSysName = sName;
324    }
325
326    String _logixNGSysName;
327
328    protected void copyBean(@Nonnull E sourceBean, @Nonnull E targetBean) {
329        throw new UnsupportedOperationException("Not implemented");
330    }
331
332    protected boolean isCopyBeanSupported() {
333        return false;
334    }
335
336    protected boolean isExecuteSupported() {
337        return false;
338    }
339
340    protected void execute(@Nonnull E bean) {
341        throw new UnsupportedOperationException("Not implemented");
342    }
343
344    /**
345     * Copy the bean as configured in the Copy set up pane.
346     *
347     * @param e the event heard
348     */
349    private void copyBeanPressed(ActionEvent e) {
350
351        String uName = _addUserName.getText().trim();
352        if (uName.length() == 0) {
353            uName = null;
354        }
355        E targetBean;
356        if (_autoSystemName.isSelected()) {
357            if (!checkLogixNGUserName(uName)) {
358                return;
359            }
360            targetBean = createBean(uName);
361        } else {
362            if (!checkLogixNGSysName()) {
363                return;
364            }
365            String sName = _systemName.getText().trim();
366            // check if a bean with this name already exists
367            boolean createLogix = true;
368            targetBean = getManager().getBySystemName(sName);
369            if (targetBean != null) {
370                int result = JmriJOptionPane.showConfirmDialog(f,
371                        Bundle.getMessage("ConfirmLogixDuplicate", sName, _logixNGSysName), // NOI18N
372                        Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION,    // NOI18N
373                        JmriJOptionPane.QUESTION_MESSAGE);
374                if (JmriJOptionPane.NO_OPTION == result) {
375                    return;
376                }
377                createLogix = false;
378                String userName = targetBean.getUserName();
379                if (userName != null && userName.length() > 0) {
380                    _addUserName.setText(userName);
381                    uName = userName;
382                }
383            } else if (!checkLogixNGUserName(uName)) {
384                return;
385            }
386            if (createLogix) {
387                // Create the new LogixNG
388                targetBean = createBean(sName, uName);
389                if (targetBean == null) {
390                    // should never get here unless there is an assignment conflict
391                    log.error("Failure to create LogixNG with System Name: {}", sName);  // NOI18N
392                    return;
393                }
394            } else if (targetBean == null) {
395                log.error("Error targetLogix is null!");  // NOI18N
396                return;
397            } else {
398                targetBean.setUserName(uName);
399            }
400        }
401        E sourceBean = getManager().getBySystemName(_logixNGSysName);
402        if (sourceBean != null) copyBean(sourceBean, targetBean);
403        else log.error("Error targetLogix is null!");  // NOI18N
404        cancelAddPressed(null);
405    }
406
407    /**
408     * Check and warn if a string is already in use as the user name of a LogixNG.
409     *
410     * @param uName the suggested name
411     * @return true if not in use
412     */
413    boolean checkLogixNGUserName(String uName) {
414        // check if a bean with the same user name exists
415        if (uName != null && uName.trim().length() > 0) {
416            E x = getManager().getByUserName(uName);
417            if (x != null) {
418                // A bean with this user name already exists
419                JmriJOptionPane.showMessageDialog(addLogixNGFrame,
420                        Bundle.getMessage("LogixNGError3"), Bundle.getMessage("ErrorTitle"), // NOI18N
421                        JmriJOptionPane.ERROR_MESSAGE);
422                return false;
423            }
424        }
425        return true;
426    }
427
428    /**
429     * Check validity of various LogixNG system names.
430     * <p>
431     * Fixes name if it doesn't start with the appropriate prefix or the $ for alpha suffixes
432     *
433     * @return false if the name fails the NameValidity check
434     */
435    boolean checkLogixNGSysName() {
436        if (_autoSystemName.isSelected()) {
437            return true;
438        }
439
440        var sName = _systemName.getText().trim();
441        var prefix = getManager().getSubSystemNamePrefix();
442
443        if (!sName.isEmpty() && !sName.startsWith(prefix)) {
444            var isNumber = sName.matches("^\\d+$");
445            var hasDollar = sName.startsWith("$");
446
447            var newName = new StringBuilder(prefix);
448            if (!isNumber && !hasDollar) {
449                newName.append("$");
450            }
451            newName.append(sName);
452            sName = newName.toString();
453        }
454
455        if (getManager().validSystemNameFormat(sName) != jmri.Manager.NameValidity.VALID) {
456            JmriJOptionPane.showMessageDialog(null,
457                    Bundle.getMessage("LogixNGError8", sName), Bundle.getMessage("ErrorTitle"), // NOI18N
458                    JmriJOptionPane.ERROR_MESSAGE);
459            return false;
460        }
461
462        _systemName.setText(sName);
463        return true;
464    }
465
466    /**
467     * Check if another bean editing session is currently open or no system
468     * name is provided.
469     *
470     * @param sName system name of bean to be copied
471     * @return true if a new session may be started
472     */
473    boolean checkFlags(String sName) {
474        if (_inEditMode) {
475            // Already editing a bean, ask for completion of that edit
476            JmriJOptionPane.showMessageDialog(null,
477                    Bundle.getMessage("LogixNGError32", _curNamedBean.getSystemName()),
478                    Bundle.getMessage("ErrorTitle"),
479                    JmriJOptionPane.ERROR_MESSAGE);
480            if (_editor != null) {
481                _editor.bringToFront();
482            }
483            return false;
484        }
485
486        if (_inCopyMode) {
487            // Already editing a bean, ask for completion of that edit
488            JmriJOptionPane.showMessageDialog(null,
489                    Bundle.getMessage("LogixNGError31", _logixNGSysName),
490                    Bundle.getMessage("ErrorTitle"), // NOI18N
491                    JmriJOptionPane.ERROR_MESSAGE);
492            return false;
493        }
494
495        if (sName != null) {
496            // check if a bean with this name exists
497            E x = getManager().getBySystemName(sName);
498            if (x == null) {
499                // bean does not exist, so cannot be edited
500                log.error("No bean with system name: {}", sName);
501                JmriJOptionPane.showMessageDialog(null,
502                        Bundle.getMessage("LogixNGError5"),
503                        Bundle.getMessage("ErrorTitle"), // NOI18N
504                        JmriJOptionPane.ERROR_MESSAGE);
505                return false;
506            }
507        }
508        return true;
509    }
510
511    /**
512     * Respond to the Create bean button in Add bean window.
513     *
514     * @param e The event heard
515     */
516    void createPressed(ActionEvent e) {
517        // possible change
518        _showReminder = true;
519        String sName;
520        String uName = _addUserName.getText().trim();
521        if (uName.length() == 0) {
522            uName = null;
523        }
524        if (_autoSystemName.isSelected()) {
525            if (!checkLogixNGUserName(uName)) {
526                return;
527            }
528            try {
529                _curNamedBean = createBean(uName);
530            } catch (BadSystemNameException | BadUserNameException ex) {
531                JmriJOptionPane.showMessageDialog(addLogixNGFrame, ex.getLocalizedMessage(),
532                        Bundle.getMessage("ErrorTitle"), // NOI18N
533                        JmriJOptionPane.ERROR_MESSAGE);
534                return;
535            }
536            if (_curNamedBean == null) {
537                log.error("Failure to create bean with System Name: {}", "none");  // NOI18N
538                return;
539            }
540            sName = _curNamedBean.getSystemName();
541        } else {
542            if (!checkLogixNGSysName()) {
543                return;
544            }
545            // Get validated system name
546            sName = _systemName.getText();
547            // check if a bean with this name already exists
548            E x;
549            try {
550                x = getManager().getBySystemName(sName);
551            } catch (Exception ex) {
552                // user input no good
553                handleCreateException(sName);
554                return;  // without creating
555            }
556            if (x != null) {
557                // bean already exists
558                JmriJOptionPane.showMessageDialog(addLogixNGFrame, Bundle.getMessage("LogixNGError1"),
559                        Bundle.getMessage("ErrorTitle"), // NOI18N
560                        JmriJOptionPane.ERROR_MESSAGE);
561                return;
562            }
563            if (!checkLogixNGUserName(uName)) {
564                return;
565            }
566            // Create the new bean
567            _curNamedBean = createBean(sName, uName);
568            if (_curNamedBean == null) {
569                // should never get here unless there is an assignment conflict
570                log.error("Failure to create bean with System Name: {}", sName);  // NOI18N
571                return;
572            }
573        }
574        cancelAddPressed(null);
575        // create the Edit bean Window
576        editPressed(sName);
577        InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> {
578            prefMgr.setCheckboxPreferenceState(systemNameAuto, _autoSystemName.isSelected());
579        });
580    }
581
582    void handleCreateException(String sysName) {
583        JmriJOptionPane.showMessageDialog(addLogixNGFrame,
584                Bundle.getMessage("ErrorLogixAddFailed", sysName), // NOI18N
585                Bundle.getMessage("ErrorTitle"), // NOI18N
586                JmriJOptionPane.ERROR_MESSAGE);
587    }
588
589    // ------------ Methods for Edit bean Pane ------------
590
591    /**
592     * Respond to the Edit button pressed in LogixNG table.
593     *
594     * @param sName system name of LogixNG to be edited
595     */
596    void editPressed(String sName) {
597        _curNamedBean = getManager().getBySystemName(sName);
598        if (!checkFlags(sName)) {
599            return;
600        }
601
602        // Create a new bean edit view, add the listener.
603        _editor = getEditor(m, sName);
604
605        if (_editor == null) return;    // Editor not implemented yet for LogixNG Tables
606
607        _inEditMode = true;
608
609        _editor.addEditorEventListener((data) -> {
610            String lgxName = sName;
611            data.forEach((key, value) -> {
612                if (key.equals("Finish")) {                  // NOI18N
613                    _editor = null;
614                    _inEditMode = false;
615                    f.setVisible(true);
616                } else if (key.equals("Delete")) {           // NOI18N
617                    _inEditMode = false;
618                    deletePressed(value);
619                } else if (key.equals("chgUname")) {         // NOI18N
620                    E x = getManager().getBySystemName(lgxName);
621                    if (x == null) {
622                        log.error("Found no logixNG for name {} when changing user name (2)", lgxName);
623                        return;
624                    }
625                    x.setUserName(value);
626                    m.fireTableDataChanged();
627                }
628            });
629        });
630    }
631
632    /**
633     * Respond to the Delete combo selection bean window delete request.
634     *
635     * @param sName system name of bean to be deleted
636     */
637    void deletePressed(String sName) {
638        if (!checkFlags(sName)) {
639            return;
640        }
641
642        final E x = getManager().getBySystemName(sName);
643
644        if (x == null) return;  // This should never happen
645
646        deleteBean.delete(x, hasChildren(x), (t)->{deleteBean(t);},
647                (t,list)->{getListenerRefsIncludingChildren(t,list);},
648                getClassName());
649    }
650
651    /**
652     * Respond to the Execute combo selection bean window execute request.
653     *
654     * @param sName system name of bean to be deleted
655     */
656    void executePressed(String sName) {
657        final E x = getManager().getBySystemName(sName);
658
659        if (x == null) return;  // This should never happen
660
661        execute(x);
662    }
663
664    @Override
665    protected String getClassName() {
666        // The class that is returned must have a default constructor,
667        // a constructor with no parameters.
668        return jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName();
669    }
670
671// ------------ Methods for Conditional Browser Window ------------
672    /**
673     * Respond to the Browse button pressed in bean table.
674     *
675     * @param sName The selected bean system name
676     */
677    void browserPressed(String sName) {
678        // bean was found, create the window
679        _curNamedBean = getManager().getBySystemName(sName);
680
681        if (_curNamedBean == null) {
682            // This should never happen but SpotBugs complains about it.
683            throw new RuntimeException("_curNamedBean is null");
684        }
685
686        String title = Bundle.getMessage("BrowserLogixNG") + " " + _curNamedBean.getSystemName() + "    " // NOI18N
687                + _curNamedBean.getUserName() + "    "
688                + (isEnabled(_curNamedBean)
689                        ? Bundle.getMessage("BrowserEnabled") // NOI18N
690                        : Bundle.getMessage("BrowserDisabled"));  // NOI18N
691
692        LogixNGBrowseWindow browseWindow =
693                new LogixNGBrowseWindow(Bundle.getMessage("LogixNG_Browse_Title"));
694        browseWindow.getPrintTreeSettings();
695        boolean showSettingsPanel = this instanceof LogixNGTableAction || this instanceof LogixNGModuleTableAction;
696        browseWindow.makeBrowserWindow(browseMonoSpace(), showSettingsPanel, title, _curNamedBean.getSystemName(),
697                (printTreeSettings) -> {
698                        return AbstractLogixNGTableAction.this.getBeanText(_curNamedBean, printTreeSettings);
699                });
700    }
701
702
703
704    protected class TableModel extends BeanTableDataModel<E> {
705
706        // overlay the state column with the edit column
707        static public final int ENABLECOL = VALUECOL;
708        static public final int EDITCOL = DELETECOL;
709        protected String enabledString = Bundle.getMessage("ColumnHeadEnabled");  // NOI18N
710
711        @Override
712        public String getColumnName(int col) {
713            if (col == EDITCOL) {
714                return Bundle.getMessage("ColumnHeadMenu");     // This makes it easier to test the table
715            }
716            if (col == ENABLECOL) {
717                return enabledString;
718            }
719            return super.getColumnName(col);
720        }
721
722        @Override
723        public Class<?> getColumnClass(int col) {
724            if (col == EDITCOL) {
725                return String.class;
726            }
727            if (col == ENABLECOL) {
728                return Boolean.class;
729            }
730            return super.getColumnClass(col);
731        }
732
733        @Override
734        public int getPreferredWidth(int col) {
735            // override default value for SystemName and UserName columns
736            if (col == SYSNAMECOL) {
737                return new JTextField(12).getPreferredSize().width;
738            }
739            if (col == USERNAMECOL) {
740                return new JTextField(17).getPreferredSize().width;
741            }
742            if (col == EDITCOL) {
743                return new JTextField(12).getPreferredSize().width;
744            }
745            if (col == ENABLECOL) {
746                return new JTextField(5).getPreferredSize().width;
747            }
748            return super.getPreferredWidth(col);
749        }
750
751        @Override
752        public boolean isCellEditable(int row, int col) {
753            if (col == EDITCOL) {
754                return true;
755            }
756            if (col == ENABLECOL) {
757                return true;
758            }
759            return super.isCellEditable(row, col);
760        }
761
762        @SuppressWarnings("unchecked")  // Unchecked cast from Object to E
763        @Override
764        public Object getValueAt(int row, int col) {
765            if (col == EDITCOL) {
766                return Bundle.getMessage("ButtonSelect");  // NOI18N
767            } else if (col == ENABLECOL) {
768                E x = (E) getValueAt(row, SYSNAMECOL);
769                if (x == null) {
770                    return null;
771                }
772                return isEnabled(x);
773            } else {
774                return super.getValueAt(row, col);
775            }
776        }
777
778        @SuppressWarnings("unchecked")  // Unchecked cast from Object to E
779        @Override
780        public void setValueAt(Object value, int row, int col) {
781            if (col == EDITCOL) {
782                // set up to edit
783                String sName = ((NamedBean) getValueAt(row, SYSNAMECOL)).getSystemName();
784                if (Bundle.getMessage("ButtonEdit").equals(value)) {  // NOI18N
785                    editPressed(sName);
786
787                } else if (Bundle.getMessage("BrowserButton").equals(value)) {  // NOI18N
788                    conditionalRowNumber = row;
789                    browserPressed(sName);
790
791                } else if (Bundle.getMessage("ButtonCopy").equals(value)) {  // NOI18N
792                    copyPressed(sName);
793
794                } else if (Bundle.getMessage("ButtonDelete").equals(value)) {  // NOI18N
795                    deletePressed(sName);
796
797                } else if (Bundle.getMessage("LogixNG_ButtonExecute").equals(value)) {  // NOI18N
798                    executePressed(sName);
799                }
800            } else if (col == ENABLECOL) {
801                // alternate
802                E x = (E) getValueAt(row, SYSNAMECOL);
803                boolean v = isEnabled(x);
804                setEnabled(x, !v);
805            } else {
806                super.setValueAt(value, row, col);
807            }
808        }
809
810        /**
811         * Delete the bean after all the checking has been done.
812         * <p>
813         * Deletes the NamedBean.
814         *
815         * @param bean of the NamedBean to delete
816         */
817        @Override
818        protected void doDelete(E bean) {
819            // delete the LogixNG
820            AbstractLogixNGTableAction.this.deleteBean(bean);
821        }
822
823        @Override
824        protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
825            if (e.getPropertyName().equals(enabledString)) {
826                return true;
827            }
828            return super.matchPropertyName(e);
829        }
830
831        @Override
832        public Manager<E> getManager() {
833            return AbstractLogixNGTableAction.this.getManager();
834        }
835
836        @Override
837        public E getBySystemName(String name) {
838            return AbstractLogixNGTableAction.this.getManager().getBySystemName(name);
839        }
840
841        @Override
842        public E getByUserName(String name) {
843            return AbstractLogixNGTableAction.this.getManager().getByUserName(name);
844        }
845
846        @Override
847        protected String getMasterClassName() {
848            return getClassName();
849        }
850
851        @Override
852        public void configureTable(JTable table) {
853            table.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer());
854            table.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
855            table.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
856            if (!(getManager() instanceof jmri.jmrit.logixng.LogixNG_Manager)) {
857                table.getColumnModel().getColumn(2).setMinWidth(0);
858                table.getColumnModel().getColumn(2).setMaxWidth(0);
859            }
860            super.configureTable(table);
861        }
862
863        /**
864         * Replace delete button with comboBox to edit/delete/copy/select NamedBean.
865         *
866         * @param table name of the NamedBean JTable holding the column
867         */
868        @Override
869        protected void configDeleteColumn(JTable table) {
870            JComboBox<String> editCombo = new JComboBox<>();
871            editCombo.addItem(Bundle.getMessage("ButtonSelect"));  // NOI18N
872            if (isEditSupported()) editCombo.addItem(Bundle.getMessage("ButtonEdit"));  // NOI18N
873            editCombo.addItem(Bundle.getMessage("BrowserButton"));  // NOI18N
874            if (isCopyBeanSupported()) editCombo.addItem(Bundle.getMessage("ButtonCopy"));  // NOI18N
875            editCombo.addItem(Bundle.getMessage("ButtonDelete"));  // NOI18N
876            if (isExecuteSupported()) editCombo.addItem(Bundle.getMessage("LogixNG_ButtonExecute"));  // NOI18N
877            TableColumn col = table.getColumnModel().getColumn(BeanTableDataModel.DELETECOL);
878            col.setCellEditor(new DefaultCellEditor(editCombo));
879        }
880
881        // Not needed - here for interface compatibility
882        @Override
883        public void clickOn(NamedBean t) {
884        }
885
886        @Override
887        public String getValue(String s) {
888            return "";
889        }
890
891        @Override
892        protected String getBeanType() {
893//                 return Bundle.getMessage("BeanNameLogix");  // NOI18N
894            return rbx.getString("BeanNameLogixNG");  // NOI18N
895        }
896    }
897
898
899    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractLogixNGTableAction.class);
900
901}