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