001package jmri.jmrix.grapevine.nodeconfig;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.awt.Container;
006import java.awt.FlowLayout;
007import java.awt.event.ActionEvent;
008import java.awt.event.ActionListener;
009
010import javax.swing.BorderFactory;
011import javax.swing.BoxLayout;
012import javax.swing.JButton;
013import javax.swing.JComboBox;
014import javax.swing.JLabel;
015import javax.swing.JPanel;
016import javax.swing.JSpinner;
017import javax.swing.SpinnerNumberModel;
018import javax.swing.border.Border;
019
020import jmri.jmrix.grapevine.SerialMessage;
021import jmri.jmrix.grapevine.SerialNode;
022import jmri.jmrix.grapevine.SerialSensorManager;
023import jmri.jmrix.grapevine.GrapevineSystemConnectionMemo;
024import jmri.util.swing.JmriJOptionPane;
025
026/**
027 * Frame for user configuration of serial nodes.
028 *
029 * @author Bob Jacobsen Copyright (C) 2004, 2007
030 * @author Dave Duchamp Copyright (C) 2004, 2006
031 */
032public class NodeConfigFrame extends jmri.util.JmriJFrame {
033
034    private GrapevineSystemConnectionMemo memo = null;
035
036    protected JSpinner nodeAddrSpinner;
037    protected JLabel nodeAddrStatic = new JLabel("000");
038    protected javax.swing.JComboBox<String> nodeTypeBox;
039
040    protected JButton addButton = new JButton(Bundle.getMessage("ButtonAdd"));
041    protected JButton editButton = new JButton(Bundle.getMessage("ButtonEdit"));
042    protected JButton deleteButton = new JButton(Bundle.getMessage("ButtonDelete"));
043    protected JButton initButton = new JButton(Bundle.getMessage("ButtonInit"));
044    protected JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));
045    protected JButton updateButton = new JButton(Bundle.getMessage("ButtonUpdate"));
046    protected JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
047
048    protected JLabel statusText1 = new JLabel();
049    protected JLabel statusText2 = new JLabel();
050    protected JLabel statusText3 = new JLabel();
051
052    protected boolean changedNode = false;  // true if a node was changed, deleted, or added
053    protected boolean editMode = false;     // true if in edit mode
054    private boolean checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled();
055
056    protected SerialNode curNode = null;    // Serial Node being editted
057    protected int nodeAddress = 0;          // Node address
058    protected int nodeType = SerialNode.NODE2002V6; // Node type
059
060    protected boolean errorInStatus1 = false;
061    protected boolean errorInStatus2 = false;
062    protected String stdStatus1 = Bundle.getMessage("NotesStd1");
063    protected String stdStatus2 = Bundle.getMessage("NotesStd2");
064    protected String stdStatus3 = Bundle.getMessage("NotesStd3");
065    protected String editStatus1 = Bundle.getMessage("NotesEdit1");
066    protected String editStatus2 = Bundle.getMessage("NotesEdit2");
067    protected String editStatus3 = Bundle.getMessage("NotesEdit3");
068
069    /**
070     * Constructor method.
071     * @param _memo system connection.
072     */
073    public NodeConfigFrame(GrapevineSystemConnectionMemo _memo) {
074        super();
075        memo = _memo;
076    }
077
078    /**
079     * Initialize the config window.
080     */
081    @Override
082    public void initComponents() {
083        setTitle(Bundle.getMessage("ConfigNodesTitle"));
084
085        Container contentPane = getContentPane();
086        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
087
088        // Set up node address and node type
089        JPanel panel1 = new JPanel();
090        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
091
092        // panel11 is the node address and type
093        JPanel panel11 = new JPanel();
094        panel11.setLayout(new FlowLayout());
095        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress") + " "));
096        nodeAddrSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
097        panel11.add(nodeAddrSpinner);
098        nodeAddrSpinner.setToolTipText(Bundle.getMessage("TipNodeAddress"));
099        panel11.add(nodeAddrStatic);
100        nodeAddrStatic.setVisible(false);
101        panel11.add(new JLabel("   " + Bundle.getMessage("LabelNodeType") + " "));
102        nodeTypeBox = new JComboBox<String>(SerialNode.getBoardNames());
103        panel11.add(nodeTypeBox);
104        nodeTypeBox.setToolTipText(Bundle.getMessage("TipNodeType"));
105        contentPane.add(panel11);
106
107        // Set up the notes panel
108        JPanel panel3 = new JPanel();
109        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
110        JPanel panel31 = new JPanel();
111        panel31.setLayout(new FlowLayout());
112        statusText1.setText(stdStatus1);
113        statusText1.setVisible(true);
114        panel31.add(statusText1);
115        JPanel panel32 = new JPanel();
116        panel32.setLayout(new FlowLayout());
117        statusText2.setText(stdStatus2);
118        statusText2.setVisible(true);
119        panel32.add(statusText2);
120        JPanel panel33 = new JPanel();
121        panel33.setLayout(new FlowLayout());
122        statusText3.setText(stdStatus3);
123        statusText3.setVisible(true);
124        panel33.add(statusText3);
125        panel3.add(panel31);
126        panel3.add(panel32);
127        panel3.add(panel33);
128        Border panel3Border = BorderFactory.createEtchedBorder();
129        Border panel3Titled = BorderFactory.createTitledBorder(panel3Border,
130                Bundle.getMessage("BoxLabelNotes"));
131        panel3.setBorder(panel3Titled);
132        contentPane.add(panel3);
133
134        // Set up buttons
135        JPanel panel4 = new JPanel();
136        panel4.setLayout(new FlowLayout());
137        addButton.setText(Bundle.getMessage("ButtonAdd"));
138        addButton.setVisible(true);
139        addButton.setToolTipText(Bundle.getMessage("TipAddButton"));
140        addButton.addActionListener(new ActionListener() {
141            @Override
142            public void actionPerformed(ActionEvent e) {
143                addButtonActionPerformed();
144            }
145        });
146        panel4.add(addButton);
147        editButton.setText(Bundle.getMessage("ButtonEdit"));
148        editButton.setVisible(true);
149        editButton.setToolTipText(Bundle.getMessage("TipEditButton"));
150        panel4.add(editButton);
151        editButton.addActionListener(new ActionListener() {
152            @Override
153            public void actionPerformed(ActionEvent e) {
154                editButtonActionPerformed();
155            }
156        });
157        panel4.add(deleteButton);
158        deleteButton.setText(Bundle.getMessage("ButtonDelete"));
159        deleteButton.setVisible(true);
160        deleteButton.setToolTipText(Bundle.getMessage("TipDeleteButton"));
161        panel4.add(deleteButton);
162        deleteButton.addActionListener(new ActionListener() {
163            @Override
164            public void actionPerformed(ActionEvent e) {
165                deleteButtonActionPerformed();
166            }
167        });
168
169        panel4.add(doneButton);
170        doneButton.setText(Bundle.getMessage("ButtonDone"));
171        doneButton.setVisible(true);
172        doneButton.setToolTipText(Bundle.getMessage("TipDoneButton"));
173        panel4.add(doneButton);
174        doneButton.addActionListener(new ActionListener() {
175            @Override
176            public void actionPerformed(ActionEvent e) {
177                doneButtonActionPerformed();
178            }
179        });
180
181        panel4.add(updateButton);
182        updateButton.setText(Bundle.getMessage("ButtonUpdate"));
183        updateButton.setVisible(true);
184        updateButton.setToolTipText(Bundle.getMessage("TipUpdateButton"));
185        panel4.add(updateButton);
186        updateButton.addActionListener(new ActionListener() {
187            @Override
188            public void actionPerformed(ActionEvent e) {
189                updateButtonActionPerformed();
190            }
191        });
192        updateButton.setVisible(false);
193
194        panel4.add(initButton);
195        initButton.setText(Bundle.getMessage("ButtonInit"));
196        initButton.setVisible(true);
197        initButton.setToolTipText(Bundle.getMessage("TipInitButton"));
198        panel4.add(initButton);
199        initButton.addActionListener(new ActionListener() {
200            @Override
201            public void actionPerformed(ActionEvent e) {
202                initButtonActionPerformed();
203            }
204        });
205        initButton.setVisible(true);
206
207        panel4.add(cancelButton);
208        cancelButton.setText(Bundle.getMessage("ButtonCancel"));
209        cancelButton.setVisible(true);
210        cancelButton.setToolTipText(Bundle.getMessage("TipCancelButton"));
211        panel4.add(cancelButton);
212        cancelButton.addActionListener(new ActionListener() {
213            @Override
214            public void actionPerformed(ActionEvent e) {
215                cancelButtonActionPerformed();
216            }
217        });
218        cancelButton.setVisible(false);
219
220        contentPane.add(panel4);
221
222        // add help menu to window
223        addHelpMenu("package.jmri.jmrix.grapevine.nodeconfig.NodeConfigFrame", true);
224
225        // pack for display
226        pack();
227    }
228
229    /**
230     * Set the node number.
231     * @param node node address.
232     */
233    public void setNodeAddress(int node) {
234        nodeAddrSpinner.setValue(node);
235    }
236
237    /**
238     * Handle Add button.
239     */
240    public void addButtonActionPerformed() {
241        // Check that a node with this address does not exist
242        int nodeAddress = readNodeAddress();
243        if (nodeAddress < 0) {
244            return;
245        }
246        // get a SerialNode corresponding to this node address if one exists
247        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
248        if (curNode != null) {
249            statusText1.setText(Bundle.getMessage("Error1", Integer.toString(nodeAddress)));
250            statusText1.setVisible(true);
251            errorInStatus1 = true;
252            resetNotes2();
253            return;
254        }
255        nodeType = nodeTypeBox.getSelectedIndex();
256
257        // all ready, create the new node
258        curNode = new SerialNode(nodeAddress, nodeType, memo.getTrafficController());
259
260        // configure the new node
261        setNodeParameters();
262        // register any orphan sensors that this node may have
263        ((SerialSensorManager)memo.getSensorManager()).registerSensorsForNode(curNode);
264        // reset after succefully adding node
265        resetNotes();
266        changedNode = true;
267        // provide user feedback
268        statusText1.setText(Bundle.getMessage("FeedBackAdd") + " "
269                + Integer.toString(nodeAddress));
270        errorInStatus1 = true;
271    }
272
273    /**
274     * Handle Edit button.
275     */
276    public void editButtonActionPerformed() {
277        // Find Serial Node address
278        nodeAddress = readNodeAddress();
279        if (nodeAddress < 0) {
280            return;
281        }
282        // get the SerialNode corresponding to this node address
283        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
284        if (curNode == null) {
285            statusText1.setText(Bundle.getMessage("Error4"));
286            statusText1.setVisible(true);
287            errorInStatus1 = true;
288            resetNotes2();
289            return;
290        }
291        // Set up static node address
292        nodeAddrStatic.setText(Integer.toString(nodeAddress));
293        nodeAddrSpinner.setVisible(false);
294        nodeAddrStatic.setVisible(true);
295        // get information for this node and set up combo box
296        nodeType = curNode.getNodeType();
297        nodeTypeBox.setSelectedIndex(nodeType);
298        // Switch buttons
299        editMode = true;
300        addButton.setVisible(false);
301        editButton.setVisible(false);
302        deleteButton.setVisible(false);
303        doneButton.setVisible(false);
304        updateButton.setVisible(true);
305        cancelButton.setVisible(true);
306        // Switch to edit notes
307        statusText1.setText(editStatus1);
308        statusText2.setText(editStatus2);
309        statusText3.setText(editStatus3);
310    }
311
312    /**
313     * Handle Init button.
314     */
315    public void initButtonActionPerformed() {
316        // Find Serial Node address
317        int nodeAddress = readNodeAddress();
318        log.debug("node read {}", nodeAddress);
319        if (nodeAddress < 0) {
320            return;
321        }
322        // get the SerialNode corresponding to this node address
323        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
324        if (curNode == null) {
325            statusText1.setText(Bundle.getMessage("Error4"));
326            statusText1.setVisible(true);
327            errorInStatus1 = true;
328            resetNotes2();
329            return;
330        }
331        // ok, do the init
332        log.debug("init packet sent to node {}", nodeAddress);
333        memo.getTrafficController().sendSerialMessage((SerialMessage) curNode.createInitPacket(), null);
334    }
335
336    /**
337     * Handle Delete button.
338     */
339    public void deleteButtonActionPerformed() {
340        // Find Serial Node address
341        int nodeAddress = readNodeAddress();
342        if (nodeAddress < 0) {
343            return;
344        }
345        // get the SerialNode corresponding to this node address
346        curNode = (SerialNode) memo.getTrafficController().getNodeFromAddress(nodeAddress);
347        if (curNode == null) {
348            statusText1.setText(Bundle.getMessage("Error4"));
349            statusText1.setVisible(true);
350            errorInStatus1 = true;
351            resetNotes2();
352            return;
353        }
354        // confirm deletion with the user
355        if (JmriJOptionPane.OK_OPTION == JmriJOptionPane.showConfirmDialog(
356                this, Bundle.getMessage("ConfirmDelete1") + "\n"
357                + Bundle.getMessage("ConfirmDelete2"), Bundle.getMessage("ConfirmDeleteTitle"),
358                JmriJOptionPane.OK_CANCEL_OPTION,
359                JmriJOptionPane.WARNING_MESSAGE)) {
360            // delete this node
361            memo.getTrafficController().deleteNode(nodeAddress);
362            // provide user feedback
363            resetNotes();
364            statusText1.setText(Bundle.getMessage("FeedBackDelete") + " "
365                    + Integer.toString(nodeAddress));
366            errorInStatus1 = true;
367            changedNode = true;
368        } else {
369            // reset as needed
370            resetNotes();
371        }
372    }
373
374    /**
375     * Handle Done button.
376     */
377    public void doneButtonActionPerformed() {
378        if (editMode) {
379            // Reset
380            editMode = false;
381            curNode = null;
382            // Switch buttons
383            addButton.setVisible(true);
384            editButton.setVisible(true);
385            deleteButton.setVisible(true);
386            doneButton.setVisible(true);
387            updateButton.setVisible(false);
388            cancelButton.setVisible(false);
389            nodeAddrSpinner.setVisible(true);
390            nodeAddrStatic.setVisible(false);
391        }
392        if (changedNode && !checkEnabled) {
393            // Remind user to Save new configuration
394            JmriJOptionPane.showMessageDialog(this,
395                    Bundle.getMessage("ReminderNode1") + "\n" + Bundle.getMessage("Reminder2"),
396                    Bundle.getMessage("ReminderTitle"),
397                    JmriJOptionPane.INFORMATION_MESSAGE);
398        }
399        setVisible(false);
400        dispose();
401    }
402
403    /**
404     * Handle Update button.
405     */
406    public void updateButtonActionPerformed() {
407        // update node information
408        nodeType = nodeTypeBox.getSelectedIndex();
409        log.debug("update performed: was {} request {}", curNode.getNodeType(), nodeType);
410        if (curNode.getNodeType() != nodeType) {
411            // node type has changed
412            curNode.setNodeType(nodeType);
413        }
414        setNodeParameters();
415        changedNode = true;
416        // Reset Edit Mode
417        editMode = false;
418        curNode = null;
419        // Switch buttons
420        addButton.setVisible(true);
421        editButton.setVisible(true);
422        deleteButton.setVisible(true);
423        doneButton.setVisible(true);
424        updateButton.setVisible(false);
425        cancelButton.setVisible(false);
426        // make node address editable again
427        nodeAddrSpinner.setVisible(true);
428        nodeAddrStatic.setVisible(false);
429        // refresh notes panel
430        statusText2.setText(stdStatus2);
431        statusText3.setText(stdStatus3);
432        // provide user feedback
433        statusText1.setText(Bundle.getMessage("FeedBackUpdate") + " "
434                + Integer.toString(nodeAddress));
435        errorInStatus1 = true;
436    }
437
438    /**
439     * Handle Cancel button.
440     */
441    public void cancelButtonActionPerformed() {
442        // Reset
443        editMode = false;
444        curNode = null;
445        // Switch buttons
446        addButton.setVisible(true);
447        editButton.setVisible(true);
448        deleteButton.setVisible(true);
449        doneButton.setVisible(true);
450        updateButton.setVisible(false);
451        cancelButton.setVisible(false);
452        // make node address editable again
453        nodeAddrSpinner.setVisible(true);
454        nodeAddrStatic.setVisible(false);
455        // refresh notes panel
456        statusText1.setText(stdStatus1);
457        statusText2.setText(stdStatus2);
458        statusText3.setText(stdStatus3);
459    }
460
461    /**
462     * Do the Done action if the window is closed early.
463     */
464    @Override
465    @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER",
466            justification = "This calls doneButtonActionPerformed which handles window closing")
467    public void windowClosing(java.awt.event.WindowEvent e) {
468        doneButtonActionPerformed();
469    }
470
471    /**
472     * Set node parameters. The node must exist, and be in 'curNode'.
473     * Also, the node type must be set and in 'nodeType'.
474     */
475    void setNodeParameters() {
476        // set curNode type
477        curNode.setNodeType(nodeType);
478        // Cause reinitialization of this Node to reflect these parameters
479        memo.getTrafficController().initializeSerialNode(curNode);
480    }
481
482    /**
483     * Reset the Notes error after error display.
484     */
485    private void resetNotes() {
486        if (errorInStatus1) {
487            if (editMode) {
488                statusText1.setText(editStatus1);
489            } else {
490                statusText1.setText(stdStatus1);
491            }
492            errorInStatus1 = false;
493        }
494        resetNotes2();
495    }
496
497    /**
498     * Reset the second line of the Notes area.
499     */
500    private void resetNotes2() {
501        if (errorInStatus2) {
502            if (editMode) {
503                statusText1.setText(editStatus2);
504            } else {
505                statusText2.setText(stdStatus2);
506            }
507            errorInStatus2 = false;
508        }
509    }
510
511    /**
512     * Read node address and check for legal range.
513     *
514     * @return a node address in the range 0-127.
515     * Valid range guarded by SpinnerNumberModel.
516     */
517    private int readNodeAddress() {
518        int addr = -1;
519        addr = (Integer) nodeAddrSpinner.getValue();
520        return (addr);
521    }
522
523    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NodeConfigFrame.class);
524
525}