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