001package jmri.jmrix.ieee802154.swing.nodeconfig;
002
003import java.awt.Container;
004import java.awt.FlowLayout;
005
006import javax.swing.BorderFactory;
007import javax.swing.BoxLayout;
008import javax.swing.JLabel;
009import javax.swing.JPanel;
010import javax.swing.border.Border;
011
012import jmri.jmrix.ieee802154.IEEE802154Node;
013import jmri.jmrix.ieee802154.IEEE802154TrafficController;
014import jmri.util.swing.JmriJOptionPane;
015
016/**
017 * Frame for user configuration of IEEE 802.15.4 nodes. 
018 * Derived from node configuration for c/mri nodes.
019 *
020 * @author Bob Jacobsen Copyright (C) 2004
021 * @author Dave Duchamp Copyright (C) 2004
022 * @author Paul Bender Copyright (C) 2013
023 */
024public class NodeConfigFrame extends jmri.util.JmriJFrame {
025
026    protected javax.swing.JComboBox<String> nodeAddrField = new javax.swing.JComboBox<String>();
027    protected javax.swing.JComboBox<String> nodeAddr64Field = new javax.swing.JComboBox<String>();
028    protected javax.swing.JButton addButton = new javax.swing.JButton(Bundle.getMessage("ButtonAdd"));
029    protected javax.swing.JButton editButton = new javax.swing.JButton(Bundle.getMessage("ButtonEdit"));
030    protected javax.swing.JButton deleteButton = new javax.swing.JButton(Bundle.getMessage("ButtonDelete"));
031    protected javax.swing.JButton doneButton = new javax.swing.JButton(Bundle.getMessage("ButtonDone"));
032    protected javax.swing.JButton updateButton = new javax.swing.JButton(Bundle.getMessage("ButtonUpdate"));
033    protected javax.swing.JButton cancelButton = new javax.swing.JButton(Bundle.getMessage("ButtonCancel"));
034
035    protected javax.swing.JLabel statusText1 = new javax.swing.JLabel();
036    protected javax.swing.JLabel statusText2 = new javax.swing.JLabel();
037    protected javax.swing.JLabel statusText3 = new javax.swing.JLabel();
038
039    protected javax.swing.JPanel panel2 = new JPanel();
040    protected javax.swing.JPanel panel2a = new JPanel();
041
042    protected boolean changedNode = false;  // true if a node was changed, deleted, or added
043    protected boolean editMode = false;     // true if in edit mode
044
045    protected IEEE802154Node curNode = null;    // IEEE802154 Node being editted
046
047    protected boolean errorInStatus1 = false;
048    protected boolean errorInStatus2 = false;
049    protected String stdStatus1 = Bundle.getMessage("NotesStd1");
050    protected String stdStatus2 = Bundle.getMessage("NotesStd2");
051    protected String stdStatus3 = Bundle.getMessage("NotesStd3");
052    protected String editStatus1 = Bundle.getMessage("NotesEdit1");
053    protected String editStatus2 = Bundle.getMessage("NotesEdit2");
054    protected String editStatus3 = Bundle.getMessage("NotesEdit3");
055
056    private IEEE802154TrafficController itc = null;
057
058    /**
059     * Constructor method
060     * @param tc connector for node
061     */
062    public NodeConfigFrame(IEEE802154TrafficController tc) {
063        super();
064        addHelpMenu("package.jmri.jmrix.ieee802154.swing.nodeconfig.NodeConfigFrame", true);
065        itc = tc;
066    }
067
068    /**
069     * Initialize the config window
070     */
071    @Override
072    public void initComponents() {
073        setTitle(Bundle.getMessage("WindowTitle"));
074        Container contentPane = getContentPane();
075        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
076
077        contentPane.add(initAddressPanel());
078        contentPane.add(initNotesPanel());
079        contentPane.add(initButtonPanel());
080
081
082        // pack for display
083        pack();
084    }
085
086
087    /*
088     * Initilaize the address panel.
089     */
090    protected JPanel initAddressPanel(){
091        // Set up node address and node type
092        JPanel panel1 = new JPanel();
093        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
094        JPanel panel11 = new JPanel();
095        panel11.setLayout(new FlowLayout());
096        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress") + " "));
097        panel11.add(nodeAddrField);
098        nodeAddrField.addActionListener(new java.awt.event.ActionListener() {
099
100            @Override
101            public void actionPerformed(java.awt.event.ActionEvent e) {
102                nodeSelected();
103            }
104        });
105        nodeAddrField.setToolTipText(Bundle.getMessage("TipNodeAddress"));
106        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress64") + " "));
107        panel11.add(nodeAddr64Field);
108        nodeAddr64Field.setToolTipText(Bundle.getMessage("TipNodeAddress64"));
109        nodeAddr64Field.addActionListener(new java.awt.event.ActionListener() {
110
111            @Override
112            public void actionPerformed(java.awt.event.ActionEvent e) {
113                nodeAddrField.setSelectedIndex(nodeAddr64Field.getSelectedIndex());
114            }
115        });
116
117        initAddressBoxes();
118        panel1.add(panel11);
119        return panel1;
120    }
121
122    
123    /*
124     * Initialize the notes panel.
125     */
126    protected JPanel initNotesPanel(){
127        // Set up the notes panel
128        JPanel panel3 = new JPanel();
129        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
130        JPanel panel31 = new JPanel();
131        panel31.setLayout(new FlowLayout());
132        statusText1.setText(stdStatus1);
133        statusText1.setVisible(true);
134        panel31.add(statusText1);
135        JPanel panel32 = new JPanel();
136        panel32.setLayout(new FlowLayout());
137        statusText2.setText(stdStatus2);
138        statusText2.setVisible(true);
139        panel32.add(statusText2);
140        JPanel panel33 = new JPanel();
141        panel33.setLayout(new FlowLayout());
142        statusText3.setText(stdStatus3);
143        statusText3.setVisible(true);
144        panel33.add(statusText3);
145        panel3.add(panel31);
146        panel3.add(panel32);
147        panel3.add(panel33);
148        Border panel3Border = BorderFactory.createEtchedBorder();
149        Border panel3Titled = BorderFactory.createTitledBorder(panel3Border,
150                Bundle.getMessage("BoxLabelNotes"));
151        panel3.setBorder(panel3Titled);
152        return panel3;
153    }
154
155    /*
156     * Initialize the Button panel.
157     */
158    protected JPanel initButtonPanel(){
159        // Set up buttons
160        JPanel panel4 = new JPanel();
161        panel4.setLayout(new FlowLayout());
162        addButton.setText(Bundle.getMessage("ButtonAdd"));
163        addButton.setVisible(true);
164        addButton.setToolTipText(Bundle.getMessage("TipAddButton"));
165        addButton.addActionListener(new java.awt.event.ActionListener() {
166            @Override
167            public void actionPerformed(java.awt.event.ActionEvent e) {
168                addButtonActionPerformed();
169            }
170        });
171        panel4.add(addButton);
172        editButton.setText(Bundle.getMessage("ButtonEdit"));
173        editButton.setVisible(true);
174        editButton.setToolTipText(Bundle.getMessage("TipEditButton"));
175        panel4.add(editButton);
176        editButton.addActionListener(new java.awt.event.ActionListener() {
177            @Override
178            public void actionPerformed(java.awt.event.ActionEvent e) {
179                editButtonActionPerformed();
180            }
181        });
182        panel4.add(deleteButton);
183        deleteButton.setText(Bundle.getMessage("ButtonDelete"));
184        deleteButton.setVisible(true);
185        deleteButton.setToolTipText(Bundle.getMessage("TipDeleteButton"));
186        panel4.add(deleteButton);
187        deleteButton.addActionListener(new java.awt.event.ActionListener() {
188            @Override
189            public void actionPerformed(java.awt.event.ActionEvent e) {
190                deleteButtonActionPerformed();
191            }
192        });
193        panel4.add(doneButton);
194        doneButton.setText(Bundle.getMessage("ButtonDone"));
195        doneButton.setVisible(true);
196        doneButton.setToolTipText(Bundle.getMessage("TipDoneButton"));
197        panel4.add(doneButton);
198        doneButton.addActionListener(new java.awt.event.ActionListener() {
199            @Override
200            public void actionPerformed(java.awt.event.ActionEvent e) {
201                doneButtonActionPerformed();
202            }
203        });
204        panel4.add(updateButton);
205        updateButton.setText(Bundle.getMessage("ButtonUpdate"));
206        updateButton.setVisible(true);
207        updateButton.setToolTipText(Bundle.getMessage("TipUpdateButton"));
208        panel4.add(updateButton);
209        updateButton.addActionListener(new java.awt.event.ActionListener() {
210            @Override
211            public void actionPerformed(java.awt.event.ActionEvent e) {
212                updateButtonActionPerformed();
213            }
214        });
215        updateButton.setVisible(false);
216        panel4.add(cancelButton);
217        cancelButton.setText(Bundle.getMessage("ButtonCancel"));
218        cancelButton.setVisible(true);
219        cancelButton.setToolTipText(Bundle.getMessage("TipCancelButton"));
220        panel4.add(cancelButton);
221        cancelButton.addActionListener(new java.awt.event.ActionListener() {
222            @Override
223            public void actionPerformed(java.awt.event.ActionEvent e) {
224                cancelButtonActionPerformed();
225            }
226        });
227        cancelButton.setVisible(false);
228        return panel4;
229    }
230
231    /**
232     * Method to handle add button
233     */
234    public void addButtonActionPerformed() {
235        // create a new Add Frame and display it.
236        jmri.util.JmriJFrame addFrame = new AddNodeFrame(itc);
237        try {
238           addFrame.initComponents();
239        } catch(Exception ex) {
240           log.error("Exception initializing Frame: {}",ex.toString());
241           return;
242        }
243        addFrame.setVisible(true);
244    }
245
246    /**
247     * Method to handle edit button
248     */
249    public void editButtonActionPerformed() {
250        // Find IEEE802154 Node address
251        String nodeAddress = readNodeAddress();
252        if (nodeAddress.equals("")) {
253            return;
254        }
255        // get the IEEE802154Node corresponding to this node address
256        curNode = (IEEE802154Node) itc.getNodeFromAddress(nodeAddress);
257        if (curNode == null) {
258            statusText1.setText(Bundle.getMessage("Error4"));
259            statusText1.setVisible(true);
260            errorInStatus1 = true;
261            resetNotes2();
262            return;
263        }
264
265        // create a new Edit Frame and display it.
266        jmri.util.JmriJFrame editFrame = new EditNodeFrame(itc,curNode);
267        try {
268           editFrame.initComponents();
269        } catch(Exception ex) {
270           log.error("Exception initializing Frame: {}",ex.toString());
271           return;
272        }
273        editFrame.setVisible(true);
274
275    }
276
277    /**
278     * Method to handle delete button
279     */
280    public void deleteButtonActionPerformed() {
281        // Find IEEE802154 Node address
282        String nodeAddress = readNodeAddress();
283        if (nodeAddress.equals("")) {
284            return;
285        }
286        // get the IEEE802154Node corresponding to this node address
287        curNode = (IEEE802154Node) itc.getNodeFromAddress(nodeAddress);
288        if (curNode == null) {
289            statusText1.setText(Bundle.getMessage("Error4"));
290            statusText1.setVisible(true);
291            errorInStatus1 = true;
292            resetNotes2();
293            return;
294        }
295        // confirm deletion with the user
296        if (JmriJOptionPane.OK_OPTION == JmriJOptionPane.showConfirmDialog(
297                this, Bundle.getMessage("ConfirmDelete1") + "\n"
298                + Bundle.getMessage("ConfirmDelete2"), Bundle.getMessage("ConfirmDeleteTitle"),
299                JmriJOptionPane.OK_CANCEL_OPTION,
300                JmriJOptionPane.WARNING_MESSAGE)) {
301            // delete this node
302            itc.deleteNode(nodeAddress);
303            // provide user feedback
304            resetNotes();
305            statusText1.setText(Bundle.getMessage("FeedBackDelete") + " " + nodeAddress);
306            errorInStatus1 = true;
307            changedNode = true;
308        } else {
309            // reset as needed
310            resetNotes();
311        }
312        initAddressBoxes();
313    }
314
315    /**
316     * Method to handle done button
317     */
318    public void doneButtonActionPerformed() {
319        if (editMode) {
320            // Reset 
321            editMode = false;
322            curNode = null;
323            // Switch buttons
324            addButton.setVisible(true);
325            editButton.setVisible(true);
326            deleteButton.setVisible(true);
327            doneButton.setVisible(true);
328            updateButton.setVisible(false);
329            cancelButton.setVisible(false);
330        }
331        if (changedNode) {
332            // Remind user to Save new configuration
333            JmriJOptionPane.showMessageDialog(this,
334                    Bundle.getMessage("Reminder1") + "\n" + Bundle.getMessage("Reminder2"),
335                    Bundle.getMessage("ReminderTitle"),
336                    JmriJOptionPane.INFORMATION_MESSAGE);
337        }
338        setVisible(false);
339        dispose();
340    }
341
342    /**
343     * Method to handle update button
344     */
345    public void updateButtonActionPerformed() {
346        // get node information from window
347
348        // check consistency of node information
349        if (!checkConsistency()) {
350            return;
351        }
352        // update node paramaters
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        // refresh notes panel
366        statusText2.setText(stdStatus2);
367        statusText3.setText(stdStatus3);
368        // provide user feedback
369        statusText1.setText(Bundle.getMessage("FeedBackUpdate") + " " + readNodeAddress());
370        errorInStatus1 = true;
371    }
372
373    /**
374     * Method to handle cancel button
375     */
376    public void cancelButtonActionPerformed() {
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        // refresh notes panel
388        statusText1.setText(stdStatus1);
389        statusText2.setText(stdStatus2);
390        statusText3.setText(stdStatus3);
391    }
392
393    /**
394     * Method to close the window when the close box is clicked
395     */
396    @Override
397    public void windowClosing(java.awt.event.WindowEvent e) {
398        doneButtonActionPerformed();
399        super.windowClosing(e);
400    }
401
402    /**
403     * Method to set node parameters The node must exist, and be in 'curNode'
404     */
405    protected void setNodeParameters() {
406    }
407
408    /**
409     * Method to reset the notes error after error display
410     */
411    private void resetNotes() {
412        if (errorInStatus1) {
413            if (editMode) {
414                statusText1.setText(editStatus1);
415            } else {
416                statusText1.setText(stdStatus1);
417            }
418            errorInStatus1 = false;
419        }
420        resetNotes2();
421    }
422
423    /**
424     * Reset the second line of Notes area
425     */
426    private void resetNotes2() {
427        if (errorInStatus2) {
428            if (editMode) {
429                statusText1.setText(editStatus2);
430            } else {
431                statusText2.setText(stdStatus2);
432            }
433            errorInStatus2 = false;
434        }
435    }
436
437    /**
438     * Read node address from the nodeAddressField or nodeAddr64Field 
439     * as appropriate and return as a string.  
440     *
441     * @return String containing the short (two byte) address of the node.
442     *         if the two byte node address is either "FF FF" or "FF FE",
443     *         returns the long (64 bit) address.
444     */
445    private String readNodeAddress() {
446        String addr = "";
447        addr = (String) nodeAddrField.getSelectedItem();
448        if (addr.equals("FF FF ") || addr.equals("FF FE ")) {
449            addr = (String) nodeAddr64Field.getSelectedItem();
450        }
451        return (addr);
452    }
453
454    /**
455     * Check for consistency errors by node type Returns 'true' if successful,
456     * 'false' if an error was detected. If an error is detected, a suitable
457     * error message is placed in the Notes area
458     * @return always true
459     */
460    protected boolean checkConsistency() {
461        return true;
462    }
463
464    // Initialize the drop down box for the address lists.
465    protected void initAddressBoxes() {
466        IEEE802154Node current = null;
467        nodeAddrField.removeAllItems();
468        nodeAddr64Field.removeAllItems();
469        for (int i = 0; i < itc.getNumNodes(); i++) {
470            current = (IEEE802154Node) itc.getNode(i);
471            nodeAddrField.insertItemAt(jmri.util.StringUtil.hexStringFromBytes(current.getUserAddress()), i);
472            nodeAddr64Field.insertItemAt(jmri.util.StringUtil.hexStringFromBytes(current.getGlobalAddress()), i);
473        }
474        nodeAddrField.insertItemAt("", 0);
475        nodeAddrField.setEditable(true);
476        nodeAddr64Field.insertItemAt("", 0);
477    }
478
479    // Update the display when the selected node changes.
480    protected void nodeSelected() {
481        nodeAddr64Field.setSelectedIndex(nodeAddrField.getSelectedIndex());
482    }
483
484    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NodeConfigFrame.class);
485
486}