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