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