001package jmri.jmrit.conditional;
002
003import java.awt.Container;
004import java.awt.Dimension;
005import java.awt.FlowLayout;
006import java.awt.event.ActionEvent;
007import java.awt.event.ActionListener;
008import java.beans.PropertyChangeListener;
009import java.util.ArrayList;
010import java.util.List;
011
012import javax.swing.*;
013import javax.swing.border.Border;
014import javax.swing.table.*;
015
016import jmri.*;
017import jmri.jmrit.sensorgroup.SensorGroupFrame;
018import jmri.util.JmriJFrame;
019import jmri.util.swing.JmriJOptionPane;
020import jmri.util.table.ButtonEditor;
021import jmri.util.table.ButtonRenderer;
022
023/**
024 * The traditional list based conditional editor based on the original editor
025 * included in LogixTableAction.
026 * <p>
027 * Conditionals now have two policies to trigger execution of their action
028 * lists:
029 * <ol>
030 *   <li>the previous policy - Trigger on change of state only
031 *   <li>the new default - Trigger on any enabled state calculation
032 * </ol>
033 * Jan 15, 2011 - Pete Cressman
034 * <p>
035 * Two additional action and variable name selection methods have been added:
036 * <ol>
037 *     <li>Single Pick List
038 *     <li>Combo Box Selection
039 * </ol>
040 * The traditional tabbed Pick List with text entry is the default method.
041 * The Options menu has been expanded to list the 3 methods.
042 * Mar 27, 2017 - Dave Sand
043 * <p>
044 * Add a Browse Option to the Logix Select Menu. This will display a window that
045 * creates a formatted list of the contents of the selected Logix with each
046 * Conditional, Variable and Action. The code is courtesy of Chuck Catania and
047 * is used with his permission. Apr 2, 2017 - Dave Sand
048 * <p>
049 * Compare with the other Conditional Edit tool {@link ConditionalTreeEdit}
050 *
051 * @author Dave Duchamp Copyright (C) 2007
052 * @author Pete Cressman Copyright (C) 2009, 2010, 2011
053 * @author Matthew Harris copyright (c) 2009
054 * @author Dave Sand copyright (c) 2017
055 */
056public class ConditionalListEdit extends ConditionalList {
057
058    /**
059     * Create a new Conditional List View editor.
060     *
061     * @param sName name of the Logix being edited
062     */
063    public ConditionalListEdit(String sName) {
064        super(sName);
065        makeEditLogixWindow();
066    }
067
068    public ConditionalListEdit() {
069    }
070
071    // ------------ Logix Variables ------------
072    JTextField _editUserName;
073    JLabel _status;
074    int _numConditionals = 0;
075
076    // ------------ Conditional Variables ------------
077    ConditionalTableModel conditionalTableModel = null;
078    int _conditionalRowNumber = 0;
079    boolean _inReorderMode = false;
080    int _nextInOrder = 0;
081
082    static final int STRUT = 10;
083
084    // ------------ Methods for Edit Logix Pane ------------
085
086    /**
087     * Create and/or initialize the Edit Logix pane.
088     */
089    void makeEditLogixWindow() {
090        _editUserName = new JTextField(20);
091        _editUserName.setText(_curLogix.getUserName());
092        // clear conditional table if needed
093        if (conditionalTableModel != null) {
094            conditionalTableModel.fireTableStructureChanged();
095        }
096        _inEditMode = true;
097        if (_editLogixFrame == null) {
098            _editLogixFrame = new JmriJFrame(Bundle.getMessage("TitleEditLogix"), false, false);  // NOI18N
099            _editLogixFrame.addHelpMenu(
100                    "package.jmri.jmrit.conditional.ConditionalListEditor", true);  // NOI18N
101            _editLogixFrame.setLocation(100, 30);
102            Container contentPane = _editLogixFrame.getContentPane();
103            contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
104            JPanel panel1 = new JPanel();
105            panel1.setLayout(new FlowLayout());
106            JLabel systemNameLabel = new JLabel(Bundle.getMessage("ColumnSystemName") + ":");  // NOI18N
107            panel1.add(systemNameLabel);
108            JLabel fixedSystemName = new JLabel(_curLogix.getSystemName());
109            panel1.add(fixedSystemName);
110            contentPane.add(panel1);
111            JPanel panel2 = new JPanel();
112            panel2.setLayout(new FlowLayout());
113            JLabel userNameLabel = new JLabel(Bundle.getMessage("ColumnUserName") + ":");  // NOI18N
114            panel2.add(userNameLabel);
115            panel2.add(_editUserName);
116            _editUserName.setToolTipText(Bundle.getMessage("LogixUserNameHint2"));  // NOI18N
117            contentPane.add(panel2);
118            // add table of Conditionals
119            JPanel pctSpace = new JPanel();
120            pctSpace.setLayout(new FlowLayout());
121            pctSpace.add(new JLabel("   "));
122            contentPane.add(pctSpace);
123            JPanel pTitle = new JPanel();
124            pTitle.setLayout(new FlowLayout());
125            pTitle.add(new JLabel(Bundle.getMessage("ConditionalTableTitle")));  // NOI18N
126            contentPane.add(pTitle);
127            // initialize table of conditionals
128            conditionalTableModel = new ConditionalTableModel();
129            JTable conditionalTable = new JTable(conditionalTableModel);
130            conditionalTable.setRowSelectionAllowed(false);
131            TableColumnModel conditionalColumnModel = conditionalTable
132                    .getColumnModel();
133            TableColumn sNameColumn = conditionalColumnModel
134                    .getColumn(ConditionalTableModel.SNAME_COLUMN);
135            sNameColumn.setResizable(true);
136            sNameColumn.setMinWidth(100);
137            sNameColumn.setPreferredWidth(130);
138            TableColumn uNameColumn = conditionalColumnModel
139                    .getColumn(ConditionalTableModel.UNAME_COLUMN);
140            uNameColumn.setResizable(true);
141            uNameColumn.setMinWidth(210);
142            uNameColumn.setPreferredWidth(260);
143            TableColumn stateColumn = conditionalColumnModel
144                    .getColumn(ConditionalTableModel.STATE_COLUMN);
145            stateColumn.setResizable(true);
146            stateColumn.setMinWidth(50);
147            stateColumn.setMaxWidth(100);
148            TableColumn buttonColumn = conditionalColumnModel
149                    .getColumn(ConditionalTableModel.BUTTON_COLUMN);
150
151            // install button renderer and editor
152            ButtonRenderer buttonRenderer = new ButtonRenderer();
153            conditionalTable.setDefaultRenderer(JButton.class, buttonRenderer);
154            TableCellEditor buttonEditor = new ButtonEditor(new JButton());
155            conditionalTable.setDefaultEditor(JButton.class, buttonEditor);
156            JButton testButton = new JButton("XXXXXX");  // NOI18N
157            conditionalTable.setRowHeight(testButton.getPreferredSize().height);
158            buttonColumn.setMinWidth(testButton.getPreferredSize().width);
159            buttonColumn.setMaxWidth(testButton.getPreferredSize().width);
160            buttonColumn.setResizable(false);
161
162            JScrollPane conditionalTableScrollPane = new JScrollPane(conditionalTable);
163            Dimension dim = conditionalTable.getPreferredSize();
164            dim.height = 450;
165            conditionalTableScrollPane.getViewport().setPreferredSize(dim);
166            contentPane.add(conditionalTableScrollPane);
167
168            // add message area between table and buttons
169            JPanel panel4 = new JPanel();
170            panel4.setLayout(new BoxLayout(panel4, BoxLayout.Y_AXIS));
171            JPanel panel41 = new JPanel();
172            panel41.setLayout(new FlowLayout());
173            _status = new JLabel(" ");
174            panel41.add(_status);
175            panel4.add(panel41);
176            JPanel panel42 = new JPanel();
177            panel42.setLayout(new FlowLayout());
178            // Conditional panel buttons - New Conditional
179            JButton newConditionalButton = new JButton(Bundle.getMessage("NewConditionalButton"));  // NOI18N
180            panel42.add(newConditionalButton);
181            newConditionalButton.addActionListener(new ActionListener() {
182                @Override
183                public void actionPerformed(ActionEvent e) {
184                    newConditionalPressed(e);
185                }
186            });
187            newConditionalButton.setToolTipText(Bundle.getMessage("NewConditionalButtonHint"));  // NOI18N
188            // Conditional panel buttons - Reorder
189            JButton reorderButton = new JButton(Bundle.getMessage("ReorderButton"));  // NOI18N
190            panel42.add(reorderButton);
191            reorderButton.addActionListener(new ActionListener() {
192                @Override
193                public void actionPerformed(ActionEvent e) {
194                    reorderPressed(e);
195                }
196            });
197            reorderButton.setToolTipText(Bundle.getMessage("ReorderButtonHint"));  // NOI18N
198            // Conditional panel buttons - Calculate
199            JButton calculateButton = new JButton(Bundle.getMessage("CalculateButton"));  // NOI18N
200            panel42.add(calculateButton);
201            calculateButton.addActionListener(new ActionListener() {
202                @Override
203                public void actionPerformed(ActionEvent e) {
204                    calculatePressed(e);
205                }
206            });
207            calculateButton.setToolTipText(Bundle.getMessage("CalculateButtonHint"));  // NOI18N
208            panel4.add(panel42);
209            Border panel4Border = BorderFactory.createEtchedBorder();
210            panel4.setBorder(panel4Border);
211            contentPane.add(panel4);
212            // add buttons at bottom of window
213            JPanel panel5 = new JPanel();
214            panel5.setLayout(new FlowLayout());
215            // Bottom Buttons - Done Logix
216            JButton done = new JButton(Bundle.getMessage("ButtonDone"));  // NOI18N
217            panel5.add(done);
218            done.addActionListener(new ActionListener() {
219                @Override
220                public void actionPerformed(ActionEvent e) {
221                    donePressed(e);
222                }
223            });
224            done.setToolTipText(Bundle.getMessage("DoneButtonHint"));  // NOI18N
225            // Delete Logix
226            JButton delete = new JButton(Bundle.getMessage("ButtonDelete"));  // NOI18N
227            panel5.add(delete);
228            delete.addActionListener(new ActionListener() {
229                @Override
230                public void actionPerformed(ActionEvent e) {
231                    deletePressed(e);
232                }
233            });
234            delete.setToolTipText(Bundle.getMessage("DeleteLogixButtonHint"));  // NOI18N
235            contentPane.add(panel5);
236        }
237
238        _editLogixFrame.addWindowListener(new java.awt.event.WindowAdapter() {
239            @Override
240            public void windowClosing(java.awt.event.WindowEvent e) {
241                donePressed(null);
242            }
243        });
244        _editLogixFrame.pack();
245        _editLogixFrame.setVisible(true);
246    }
247
248    /**
249     * Respond to the New Conditional Button in Edit Logix Window.
250     *
251     * @param e The event heard
252     */
253    void newConditionalPressed(ActionEvent e) {
254        if (checkEditConditional()) {
255            return;
256        }
257        if (_curLogix.getSystemName().equals(SensorGroupFrame.logixSysName)) {
258            JmriJOptionPane.showMessageDialog(_editLogixFrame,
259                    Bundle.getMessage("Warn8", SensorGroupFrame.logixUserName, SensorGroupFrame.logixSysName),
260                    Bundle.getMessage("WarningTitle"), // NOI18N
261                    JmriJOptionPane.WARNING_MESSAGE);
262            return;
263        }
264        _curConditional = makeNewConditional(_curLogix);
265        makeEditConditionalWindow();
266    }
267
268    @Override
269    void updateConditionalTableModel() {
270        log.debug("updateConditionalTableModel");
271        _numConditionals = _curLogix.getNumConditionals();
272        conditionalTableModel.fireTableRowsInserted(_numConditionals, _numConditionals);
273    }
274
275    /**
276     * Respond to Edit Button in the Conditional table of the Edit Logix Window.
277     *
278     * @param rx index (row number) of Conditional to be edited
279     */
280    void editConditionalPressed(int rx) {
281        if (checkEditConditional()) {
282            return;
283        }
284        // get Conditional to edit
285        _curConditional = _conditionalManager.getBySystemName(_curLogix.getConditionalByNumberOrder(rx));
286        if (_curConditional == null) {
287            log.error("Attempted edit of non-existant conditional.");  // NOI18N
288            return;
289        }
290        _conditionalRowNumber = rx;
291        makeEditConditionalWindow();
292    }
293
294    /**
295     * Respond to the Reorder Button in the Edit Logix pane.
296     *
297     * @param e The event heard
298     */
299    void reorderPressed(ActionEvent e) {
300        if (checkEditConditional()) {
301            return;
302        }
303        // Check if reorder is reasonable
304        _showReminder = true;
305        _nextInOrder = 0;
306        _inReorderMode = true;
307        _status.setText(Bundle.getMessage("ReorderMessage"));  // NOI18N
308        conditionalTableModel.fireTableDataChanged();
309    }
310
311    /**
312     * Responds to the Calculate Button in the Edit Logix window.
313     *
314     * @param e The event heard
315     */
316    void calculatePressed(ActionEvent e) {
317        if (checkEditConditional()) {
318            return;
319        }
320        // are there Conditionals to calculate?
321        if (_numConditionals > 0) {
322            // There are conditionals to calculate
323            String cName = "";
324            Conditional c = null;
325            for (int i = 0; i < _numConditionals; i++) {
326                cName = _curLogix.getConditionalByNumberOrder(i);
327                if (cName != null) {
328                    c = _conditionalManager.getBySystemName(cName);
329                    if (c == null) {
330                        log.error("Invalid conditional system name when calculating - {}", cName);
331                    } else {
332                        // calculate without taking any action
333                        c.calculate(false, null);
334                    }
335                } else {
336                    log.error("null conditional system name when calculating");  // NOI18N
337                }
338            }
339            // force the table to update
340            conditionalTableModel.fireTableDataChanged();
341        }
342    }
343
344    /**
345     * Respond to the Done button in the Edit Logix window.
346     * <p>
347     * Note: We also get here if the Edit Logix window is dismissed, or if the
348     * Add button is pressed in the Logic Table with an active Edit Logix
349     * window.
350     *
351     * @param e The event heard
352     */
353    void donePressed(ActionEvent e) {
354        if (checkEditConditional()) {
355            return;
356        }
357        if (_curLogix.getSystemName().equals(SensorGroupFrame.logixSysName)) {
358            finishDone();
359            return;
360        }
361        // Check if the User Name has been changed
362        String uName = _editUserName.getText().trim();
363        if (!(uName.equals(_curLogix.getUserName()))) {
364            // user name has changed - check if already in use
365            if (uName.length() > 0) {
366                Logix p = _logixManager.getByUserName(uName);
367                if (p != null) {
368                    // Logix with this user name already exists
369                    log.error("Failure to update Logix with Duplicate User Name: {}", uName);
370                    JmriJOptionPane.showMessageDialog(_editLogixFrame,
371                            Bundle.getMessage("Error6"),
372                            Bundle.getMessage("ErrorTitle"), // NOI18N
373                            JmriJOptionPane.ERROR_MESSAGE);
374                    return;
375                }
376            }
377            // user name is unique, change it
378            // user name is unique, change it
379            logixData.clear();
380            logixData.put("chgUname", uName);  // NOI18N
381            fireLogixEvent();
382        }
383        // complete update and activate Logix
384        finishDone();
385    }
386
387    void finishDone() {
388        showSaveReminder();
389        _inEditMode = false;
390        if (_editLogixFrame != null) {
391            _editLogixFrame.setVisible(false);
392            _editLogixFrame.dispose();
393            _editLogixFrame = null;
394        }
395        logixData.clear();
396        logixData.put("Finish", _curLogix.getSystemName());   // NOI18N
397        fireLogixEvent();
398    }
399
400    /**
401     * Respond to the Delete button in the Edit Logix window.
402     *
403     * @param e The event heard
404     */
405    void deletePressed(ActionEvent e) {
406        if (checkEditConditional()) {
407            return;
408        }
409        if (!checkConditionalReferences(_curLogix.getSystemName())) {
410            return;
411        }
412        _showReminder = true;
413        logixData.clear();
414        logixData.put("Delete", _curLogix.getSystemName());   // NOI18N
415        fireLogixEvent();
416        finishDone();
417    }
418
419    /**
420     * Respond to the Delete Conditional Button in the Edit Conditional window.
421     */
422    void deleteConditionalPressed() {
423        if (_curConditional == null) {
424            return;
425        }
426        String sName = _curConditional.getSystemName();
427
428        if (!_newConditional) {
429            _showReminder = true;
430            _curConditional = null;
431            _numConditionals--;
432            loadReferenceNames(_conditionalFrame._variableList, _oldTargetNames);
433            String[] msgs = _curLogix.deleteConditional(sName);
434            if (msgs != null) {
435                JmriJOptionPane.showMessageDialog(_editLogixFrame,
436                        Bundle.getMessage("Error11", (Object[]) msgs), // NOI18N
437                        Bundle.getMessage("ErrorTitle"),
438                        JmriJOptionPane.ERROR_MESSAGE);  // NOI18N
439            }
440            conditionalTableModel.fireTableRowsDeleted(_conditionalRowNumber,
441                    _conditionalRowNumber);
442            if (_numConditionals < 1 && !_suppressReminder) {
443                // warning message - last Conditional deleted
444                JmriJOptionPane.showMessageDialog(_editLogixFrame,
445                        Bundle.getMessage("Warn1"),
446                        Bundle.getMessage("WarningTitle"), // NOI18N
447                        JmriJOptionPane.WARNING_MESSAGE);
448            }
449        }
450        _newConditional = false;
451        // complete deletion
452        if (_pickTables != null) {
453            _pickTables.dispose();
454            _pickTables = null;
455        }
456        closeConditionalFrame();
457    }
458
459    /**
460     * Check if edit of a conditional is in progress.
461     *
462     * @return true if this is the case, after showing dialog to user
463     */
464    boolean checkEditConditional() {
465        if (_conditionalFrame != null) {
466            if (_conditionalFrame._dataChanged) {
467                // Already editing a Conditional, ask for completion of that edit
468                JmriJOptionPane.showMessageDialog(_conditionalFrame,
469                        Bundle.getMessage("Error34", _curConditional.getSystemName()), // NOI18N
470                        Bundle.getMessage("ErrorTitle"), // NOI18N
471                        JmriJOptionPane.ERROR_MESSAGE);
472                return true;
473            } else {
474                _conditionalFrame.cancelConditionalPressed();
475            }
476        }
477        return false;
478    }
479
480    // ============ Edit Conditional Window and Methods ============
481
482    /**
483     * Create and/or initialize the Edit Conditional window.
484     * <p>
485     * Note: you can get here via the New Conditional button
486     * (newConditionalPressed) or via an Edit button in the Conditional table of
487     * the Edit Logix window.
488     */
489    void makeEditConditionalWindow() {
490        // deactivate this Logix
491        _curLogix.deActivateLogix();
492
493        _conditionalFrame = new ConditionalEditFrame(Bundle.getMessage("TitleEditConditional"), _curConditional, this);  // NOI18N
494        _oldTargetNames.clear();
495        loadReferenceNames(_conditionalFrame._variableList, _oldTargetNames);
496
497        _conditionalFrame.pack();
498        _conditionalFrame.setVisible(true);
499        InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(_editLogixFrame, null, _conditionalFrame);
500    }
501
502    /**
503     * Make the bottom panel for _conditionalFrame to hold buttons for
504     * Update/Save, Cancel, Delete/FullEdit
505     *
506     * @return the panel
507     */
508    @Override
509    JPanel makeBottomPanel() {
510        JPanel panel = new JPanel();
511
512        JButton updateButton = new JButton(Bundle.getMessage("ButtonUpdate"));  // NOI18N
513        panel.add(updateButton);
514        updateButton.addActionListener(new ActionListener() {
515            @Override
516            public void actionPerformed(ActionEvent e) {
517                _conditionalFrame.updateConditionalPressed(e);
518            }
519        });
520        updateButton.setToolTipText(Bundle.getMessage("UpdateConditionalButtonHint"));  // NOI18N
521        // Cancel
522        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));  // NOI18N
523        panel.add(cancelButton);
524        cancelButton.addActionListener(new ActionListener() {
525            @Override
526            public void actionPerformed(ActionEvent e) {
527                _conditionalFrame.cancelConditionalPressed();
528            }
529        });
530        cancelButton.setToolTipText(Bundle.getMessage("CancelConditionalButtonHint"));  // NOI18N
531
532        // add Delete Conditional button to bottom panel
533        JButton deleteButton = new JButton(Bundle.getMessage("ButtonDelete"));  // NOI18N
534        panel.add(deleteButton);
535        deleteButton.addActionListener(new ActionListener() {
536            @Override
537            public void actionPerformed(ActionEvent e) {
538                deleteConditionalPressed();
539            }
540        });
541        deleteButton.setToolTipText(Bundle.getMessage("DeleteConditionalButtonHint"));  // NOI18N
542
543        return panel;
544    }
545
546    @Override
547    boolean updateConditional(String uName, Conditional.AntecedentOperator logicType, boolean trigger, String antecedent) {
548        log.debug("updateConditional");
549        return super.updateConditional(uName, _curLogix, logicType, trigger, antecedent);
550    }
551
552    @Override
553    void closeConditionalFrame() {
554        log.debug("closeConditionalFrame()");
555        conditionalTableModel.fireTableDataChanged();
556        super.closeConditionalFrame(_curLogix);
557    }
558
559    boolean checkConditionalUserName(String uName) {
560        if ((uName != null) && (!(uName.equals("")))) {
561            Conditional p = _conditionalManager.getByUserName(_curLogix, uName);
562            if (p != null) {
563                // Conditional with this user name already exists
564                log.error("Failure to update Conditional with Duplicate User Name: {}", uName);
565                JmriJOptionPane.showMessageDialog(_conditionalFrame,
566                        Bundle.getMessage("Error10"),    // NOI18N
567                        Bundle.getMessage("ErrorTitle"), // NOI18N
568                        JmriJOptionPane.ERROR_MESSAGE);
569                return false;
570            }
571        } // else return false;
572        return true;
573    }
574
575    /**
576     * Respond to the First/Next (Delete/Reorder) Button in the ConditionalTableModel.
577     *
578     * @param row index of the row to put as next in line (instead of the one
579     *            that was supposed to be next)
580     */
581    void swapConditional(int row) {
582        _curLogix.swapConditional(_nextInOrder, row);
583        _nextInOrder++;
584        if (_nextInOrder >= _numConditionals) {
585            _inReorderMode = false;
586        }
587        //status.setText("");
588        conditionalTableModel.fireTableDataChanged();
589    }
590
591    /**
592     * Table model for Conditionals in the Edit Logix pane.
593     */
594    public class ConditionalTableModel extends AbstractTableModel implements
595            PropertyChangeListener {
596
597        public static final int SNAME_COLUMN = 0;
598
599        public static final int UNAME_COLUMN = 1;
600
601        public static final int STATE_COLUMN = 2;
602
603        public static final int BUTTON_COLUMN = 3;
604
605        public ConditionalTableModel() {
606            super();
607            _conditionalManager.addPropertyChangeListener(this);
608            updateConditionalListeners();
609        }
610
611        synchronized void updateConditionalListeners() {
612            // first, remove listeners from the individual objects
613            String sNam = "";
614            Conditional c = null;
615            _numConditionals = _curLogix.getNumConditionals();
616            for (int i = 0; i < _numConditionals; i++) {
617                // if object has been deleted, it's not here; ignore it
618                sNam = _curLogix.getConditionalByNumberOrder(i);
619                c = _conditionalManager.getBySystemName(sNam);
620                if (c != null) {
621                    c.removePropertyChangeListener(this);
622                }
623            }
624            // and add them back in
625            for (int i = 0; i < _numConditionals; i++) {
626                sNam = _curLogix.getConditionalByNumberOrder(i);
627                c = _conditionalManager.getBySystemName(sNam);
628                if (c != null) {
629                    c.addPropertyChangeListener(this);
630                }
631            }
632        }
633
634        @Override
635        public void propertyChange(java.beans.PropertyChangeEvent e) {
636            if (e.getPropertyName().equals("length")) {  // NOI18N
637                // a new NamedBean is available in the manager
638                updateConditionalListeners();
639                fireTableDataChanged();
640            } else if (matchPropertyName(e)) {
641                // a value changed.
642                fireTableDataChanged();
643            }
644        }
645
646        /**
647         * Check if this property event is announcing a change this table should
648         * display.
649         * <p>
650         * Note that events will come both from the NamedBeans and from the
651         * manager.
652         *
653         * @param e the event heard
654         * @return true if a change in State or Appearance was heard
655         */
656        boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
657            return (e.getPropertyName().contains("State") ||     // NOI18N
658                    e.getPropertyName().contains("Appearance")); // NOI18N
659        }
660
661        @Override
662        public Class<?> getColumnClass(int c) {
663            if (c == BUTTON_COLUMN) {
664                return JButton.class;
665            }
666            return String.class;
667        }
668
669        @Override
670        public int getColumnCount() {
671            return 4;
672        }
673
674        @Override
675        public int getRowCount() {
676            return (_numConditionals);
677        }
678
679        @Override
680        public boolean isCellEditable(int r, int c) {
681            if (!_inReorderMode) {
682                return ((c == UNAME_COLUMN) || (c == BUTTON_COLUMN));
683            } else if (c == BUTTON_COLUMN) {
684                return (r >= _nextInOrder);
685            }
686            return false;
687        }
688
689        @Override
690        public String getColumnName(int col) {
691            switch (col) {
692                case SNAME_COLUMN:
693                    return Bundle.getMessage("ColumnSystemName");  // NOI18N
694                case UNAME_COLUMN:
695                    return Bundle.getMessage("ColumnUserName");  // NOI18N
696                case BUTTON_COLUMN:
697                    return ""; // no label
698                case STATE_COLUMN:
699                    return Bundle.getMessage("ColumnState");  // NOI18N
700                default:
701                    return "";
702            }
703        }
704
705        @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
706                justification = "better to keep cases in column order rather than to combine")
707        public int getPreferredWidth(int col) {
708            switch (col) {
709                case SNAME_COLUMN:
710                case BUTTON_COLUMN:
711                    return new JTextField(6).getPreferredSize().width;
712                case UNAME_COLUMN:
713                    return new JTextField(17).getPreferredSize().width;
714                case STATE_COLUMN:
715                    return new JTextField(12).getPreferredSize().width;
716                default:
717                    return new JTextField(5).getPreferredSize().width;
718            }
719        }
720
721        @Override
722        public Object getValueAt(int row, int col) {
723            if ((row > _numConditionals) || (_curLogix == null)) {
724                return null;
725            }
726            switch (col) {
727                case BUTTON_COLUMN:
728                    if (!_inReorderMode) {
729                        return Bundle.getMessage("ButtonEdit");  // NOI18N
730                    } else if (_nextInOrder == 0) {
731                        return Bundle.getMessage("ButtonFirst");  // NOI18N
732                    } else if (_nextInOrder <= row) {
733                        return Bundle.getMessage("ButtonNext");  // NOI18N
734                    } else {
735                        return Integer.toString(row + 1);
736                    }
737                case SNAME_COLUMN:
738                    return _curLogix.getConditionalByNumberOrder(row);
739                case UNAME_COLUMN: {
740                    //log.debug("ConditionalTableModel: {}", _curLogix.getConditionalByNumberOrder(row));  // NOI18N
741                    Conditional c = _conditionalManager.getBySystemName(
742                            _curLogix.getConditionalByNumberOrder(row));
743                    if (c != null) {
744                        return c.getUserName();
745                    }
746                    return "";
747                }
748                case STATE_COLUMN:
749                    Conditional c = _conditionalManager.getBySystemName(
750                            _curLogix.getConditionalByNumberOrder(row));
751                    if (c != null) {
752                        int curState = c.getState();
753                        if (curState == Conditional.TRUE) {
754                            return Bundle.getMessage("True");  // NOI18N
755                        }
756                        if (curState == Conditional.FALSE) {
757                            return Bundle.getMessage("False");  // NOI18N
758                        }
759                    }
760                    return Bundle.getMessage("BeanStateUnknown");  // NOI18N
761                default:
762                    return Bundle.getMessage("BeanStateUnknown");  // NOI18N
763            }
764        }
765
766        @Override
767        public void setValueAt(Object value, int row, int col) {
768            if ((row > _numConditionals) || (_curLogix == null)) {
769                return;
770            }
771            if (col == BUTTON_COLUMN) {
772                if (_inReorderMode) {
773                    swapConditional(row);
774                } else if (_curLogix.getSystemName().equals(SensorGroupFrame.logixSysName)) {
775                    JmriJOptionPane.showMessageDialog(_conditionalFrame,
776                            Bundle.getMessage("Warn8", SensorGroupFrame.logixUserName, SensorGroupFrame.logixSysName),
777                            Bundle.getMessage("WarningTitle"),
778                            JmriJOptionPane.WARNING_MESSAGE);  // NOI18N
779                } else {
780                    // Use separate Runnable so window is created on top
781                    class WindowMaker implements Runnable {
782
783                        private int _row;
784
785                        WindowMaker(int r) {
786                            _row = r;
787                        }
788
789                        @Override
790                        public void run() {
791                            editConditionalPressed(_row);
792                        }
793                    }
794                    WindowMaker t = new WindowMaker(row);
795                    javax.swing.SwingUtilities.invokeLater(t);
796                }
797            } else if (col == UNAME_COLUMN) {
798                String uName = (String) value;
799                Conditional cn = _conditionalManager.getByUserName(_curLogix, uName);
800                if (cn == null) {
801                    String sName = _curLogix.getConditionalByNumberOrder(row);
802                    Conditional cdl = _conditionalManager.getBySystemName(sName);
803                    if (cdl==null){
804                        log.error("No conditional {} while editing user name",sName);
805                        return;
806                    }
807                    cdl.setUserName(uName);
808                    fireTableRowsUpdated(row, row);
809
810                    // Update any conditional references
811                    ArrayList<String> refList = InstanceManager.getDefault(jmri.ConditionalManager.class).getWhereUsed(sName);
812                    if (refList != null) {
813                        for (String ref : refList) {
814                            Conditional cRef = _conditionalManager.getBySystemName(ref);
815                            if (cRef==null){
816                                continue;
817                            }
818                            List<ConditionalVariable> varList = cRef.getCopyOfStateVariables();
819                            for (ConditionalVariable var : varList) {
820                                // Find the affected conditional variable
821                                if (var.getName().equals(sName)) {
822                                    var.setGuiName( (uName.length() > 0) ? uName : sName );
823                                }
824                            }
825                            cRef.setStateVariables(varList);
826                        }
827                    }
828                } else {
829                    // Duplicate user name
830                    String svName = _curLogix.getConditionalByNumberOrder(row);
831                    if (cn != _conditionalManager.getBySystemName(svName)) {
832                        messageDuplicateConditionalUserName(cn.getSystemName());
833                    }
834                }
835            }
836        }
837    }
838
839
840    @Override
841    protected String getClassName() {
842        return ConditionalListEdit.class.getName();
843    }
844
845    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConditionalListEdit.class);
846
847}