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