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