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