001package jmri.jmrix.cmri.serial.nodeiolist; 002 003import java.awt.*; 004import java.awt.event.ActionEvent; 005import java.awt.event.ActionListener; 006import java.io.IOException; 007import java.util.ArrayList; 008 009import javax.swing.*; 010import javax.swing.border.Border; 011import javax.swing.table.*; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016import jmri.jmrix.cmri.CMRISystemConnectionMemo; 017import jmri.jmrix.cmri.serial.SerialNode; 018import jmri.util.davidflanagan.CompatibleHardcopyWriter; 019 020/** 021 * Frame for running CMRI assignment list. 022 * 023 * @author Dave Duchamp Copyright (C) 2006 024 * @author Chuck Catania Copyright (C) 2014, 2016, 2017 025 */ 026public class NodeIOListFrame extends jmri.util.JmriJFrame { 027 028 ArrayList<SerialNode> cmriNode = new ArrayList<SerialNode>(); 029 030 protected boolean inputSelected = true; // true if displaying input assignments, false for output 031 protected SerialNode selNode = null; 032 public int selNodeNum = 0; // Address (ua) of selected Node 033 public int numBits = 48; // number of bits in assignment table 034 public int numInputBits = 24; // number of input bits for selected node 035 public int numOutputBits = 48; // number of output bits for selected node 036 037 // node select pane items 038 JLabel nodeLabel = new JLabel(Bundle.getMessage("NodeBoxLabel") + " "); 039 JLabel nodeAddrBox = new JLabel("??"); 040 ButtonGroup bitTypeGroup = new ButtonGroup(); 041 JRadioButton inputBits = new JRadioButton(Bundle.getMessage("ShowInputButton") + " ", true); 042 JRadioButton outputBits = new JRadioButton(Bundle.getMessage("ShowOutputButton"), false); 043 JLabel nodeInfoText = new JLabel("Node Information Text"); 044 045 JLabel nodeDesc = new JLabel("Description:"); 046 047 // assignment pane items 048 protected JPanel assignmentPanel = null; 049 protected Border inputBorder = BorderFactory.createEtchedBorder(); 050 protected Border inputBorderTitled = BorderFactory.createTitledBorder(inputBorder, 051 Bundle.getMessage("AssignmentPanelInputName")); 052 protected Border outputBorder = BorderFactory.createEtchedBorder(); 053 protected Border outputBorderTitled = BorderFactory.createTitledBorder(outputBorder, 054 Bundle.getMessage("AssignmentPanelOutputName")); 055 protected JTable assignmentTable = null; 056 protected TableModel assignmentListModel = null; 057 058 // button pane items 059 JButton printButton = new JButton(Bundle.getMessage("PrintButtonText")); 060 JButton doneButton = new JButton(Bundle.getMessage("DoneButtonText")); 061 062 NodeIOListFrame curFrame; 063 private CMRISystemConnectionMemo _memo = null; 064 065 public NodeIOListFrame(CMRISystemConnectionMemo memo) { 066 super(); 067 _memo = memo; 068 curFrame = this; 069 } 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public void initComponents() { 076 077 // set the frame's initial state 078 setTitle(Bundle.getMessage("WindowTitle") + Bundle.getMessage("WindowConnectionMemo") + _memo.getUserName()); // NOI18N 079 setSize(500, 300); 080 Container contentPane = getContentPane(); 081 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 082 083 // Set up the node selection panel 084 initializeNodes(); 085 if (cmriNode.size() > 0) { 086 inputBits.addActionListener(new ActionListener() { 087 @Override 088 public void actionPerformed(ActionEvent event) { 089 if (inputSelected == false) { 090 inputSelected = true; 091 displayNodeIOBits(selNodeNum); 092 } 093 } 094 }); 095 outputBits.addActionListener(new ActionListener() { 096 @Override 097 public void actionPerformed(ActionEvent event) { 098 if (inputSelected == true) { 099 inputSelected = false; 100 displayNodeIOBits(selNodeNum); 101 } 102 } 103 }); 104 } else { 105 nodeInfoText.setText(Bundle.getMessage("NoNodesError")); 106 } 107 nodeLabel.setToolTipText(Bundle.getMessage("NodeBoxTip")); 108 inputBits.setToolTipText(Bundle.getMessage("ShowInputTip")); 109 outputBits.setToolTipText(Bundle.getMessage("ShowOutputTip")); 110 111 JPanel panel1 = new JPanel(); 112 panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS)); 113 JPanel panel11 = new JPanel(); 114 panel11.add(nodeLabel); 115 bitTypeGroup.add(outputBits); 116 bitTypeGroup.add(inputBits); 117 panel11.add(inputBits); 118 panel11.add(outputBits); 119 JPanel panel12 = new JPanel(); 120 panel12.add(nodeInfoText); 121 122 JPanel panel13 = new JPanel(); 123 panel13.add(nodeDesc); 124 panel13.setVisible(true); 125 nodeDesc.setVisible(false); 126 panel1.add(panel13); 127 128 panel1.add(panel11); 129 panel1.add(panel12); 130 131 Border panel1Border = BorderFactory.createEtchedBorder(); 132 Border panel1Titled = BorderFactory.createTitledBorder(panel1Border, Bundle.getMessage("NodePanelName") + " " + _memo.getUserName()); 133 panel1.setBorder(panel1Titled); 134 contentPane.add(panel1); 135 136// Set up the assignment panel 137 assignmentPanel = new JPanel(); 138 assignmentPanel.setLayout(new BoxLayout(assignmentPanel, BoxLayout.Y_AXIS)); 139 assignmentListModel = new AssignmentTableModel(); 140 assignmentTable = new JTable(assignmentListModel); 141 assignmentTable.setRowSelectionAllowed(false); 142 assignmentTable.setPreferredScrollableViewportSize(new java.awt.Dimension(300, 350)); 143 144 TableColumnModel assignmentColumnModel = assignmentTable.getColumnModel(); 145 TableColumn bitColumn = assignmentColumnModel.getColumn(AssignmentTableModel.BIT_COLUMN); 146 bitColumn.setMinWidth(20); 147 bitColumn.setMaxWidth(40); 148 bitColumn.setResizable(true); 149 TableColumn addressColumn = assignmentColumnModel.getColumn(AssignmentTableModel.ADDRESS_COLUMN); 150 addressColumn.setMinWidth(40); 151 addressColumn.setMaxWidth(85); 152 addressColumn.setResizable(true); 153 TableColumn sysColumn = assignmentColumnModel.getColumn(AssignmentTableModel.SYSNAME_COLUMN); 154 sysColumn.setMinWidth(90); 155 sysColumn.setMaxWidth(150); 156 sysColumn.setResizable(true); 157 TableColumn userColumn = assignmentColumnModel.getColumn(AssignmentTableModel.USERNAME_COLUMN); 158 userColumn.setMinWidth(90); 159 userColumn.setMaxWidth(250); 160 userColumn.setResizable(true); 161 assignmentColumnModel.getColumn(AssignmentTableModel.COMMENT_COLUMN); 162 userColumn.setMinWidth(90); 163 userColumn.setMaxWidth(250); 164 userColumn.setResizable(true); 165 JScrollPane assignmentScrollPane = new JScrollPane(assignmentTable); 166 assignmentPanel.add(assignmentScrollPane, BorderLayout.CENTER); 167 if (inputSelected) { 168 assignmentPanel.setBorder(inputBorderTitled); 169 } else { 170 assignmentPanel.setBorder(outputBorderTitled); 171 } 172 contentPane.add(assignmentPanel); 173 174 // Set up Print button 175 JPanel panel3 = new JPanel(); 176 panel3.setLayout(new FlowLayout()); 177 printButton.setVisible(true); 178 printButton.setToolTipText(Bundle.getMessage("PrintButtonTip")); 179 if (cmriNode.size() > 0) { 180 printButton.addActionListener(new java.awt.event.ActionListener() { 181 @Override 182 public void actionPerformed(java.awt.event.ActionEvent e) { 183 printButtonActionPerformed(e); 184 } 185 }); 186 } 187 panel3.add(printButton); 188 contentPane.add(panel3); 189 190 doneButton.setVisible(true); 191 doneButton.setToolTipText(Bundle.getMessage("DoneButtonTip")); 192 if (cmriNode.size() > 0) { 193 doneButton.addActionListener(new java.awt.event.ActionListener() { 194 @Override 195 public void actionPerformed(java.awt.event.ActionEvent e) { 196 doneButtonActionPerformed(); 197 } 198 }); 199 } 200 panel3.add(doneButton); 201 202 addHelpMenu("package.jmri.jmrix.cmri.serial.assignment.ListFrame", true); 203 204 // pack for display 205 pack(); 206 } 207 208 /** 209 * Method to initialize configured nodes and set up the node select combo 210 * box 211 */ 212 public void initializeNodes() { 213 // get all configured nodes 214 215 SerialNode node = (SerialNode) _memo.getTrafficController().getNode(0); 216 217 int index = 0; //1 218 while (node != null) { 219 cmriNode.add(node); 220 node = (SerialNode) _memo.getTrafficController().getNode(index); 221 index++; 222 } 223// node = (SerialNode) SerialTrafficController.instance().getNode(index); 224// index ++; 225 } 226 227 /** 228 * Method to handle selection of a Node for info display. 229 * @param nodeID node ID number. 230 */ 231 public void displayNodeIOBits(int nodeID) { 232 nodeLabel.setText("Node: " + nodeID + " "); 233 selNodeNum = nodeID; 234 nodeDesc.setText(""); 235 for (int i = 0; i < cmriNode.size(); i++) { 236 if (cmriNode.get(i).getNodeAddress() == nodeID) { 237 selNode = cmriNode.get(i); 238 } 239 } 240 String nodeType = ""; 241 int type = selNode.getNodeType(); 242 int bitsPerCard = selNode.getNumBitsPerCard(); 243 int numInputCards = selNode.numInputCards(); 244 int numOutputCards = selNode.numOutputCards(); 245 246 switch (type) { 247 case SerialNode.SMINI: 248 numInputBits = 24; 249 numOutputBits = 48; 250 nodeType = "SMINI - "; 251 break; 252 case SerialNode.USIC_SUSIC: 253 numInputBits = bitsPerCard * numInputCards; 254 numOutputBits = bitsPerCard * numOutputCards; 255 nodeType = "USIC_SUSIC - "; 256 break; 257 case SerialNode.CPNODE: 258 numInputBits = bitsPerCard * numInputCards; 259 numOutputBits = bitsPerCard * numOutputCards; 260 nodeType = "CPNODE - "; 261 break; 262 case SerialNode.CPMEGA: 263 numInputBits = bitsPerCard * numInputCards; 264 numOutputBits = bitsPerCard * numOutputCards; 265 nodeType = "CPMEGA - "; 266 break; 267 case SerialNode.ESP32NODE: 268 numInputBits = bitsPerCard * numInputCards; 269 numOutputBits = bitsPerCard * numOutputCards; 270 nodeType = "ESP32Node - "; 271 break; 272 default: 273 break; 274 } 275 nodeInfoText.setText(nodeType + bitsPerCard + " " + Bundle.getMessage("BitsPerCard") + ", " 276 + numInputBits + " " + Bundle.getMessage("InputBitsAnd") + " " 277 + numOutputBits + " " + Bundle.getMessage("OutputBits")); 278 String name = selNode.getcmriNodeDesc(); 279 if (name != null && (!name.isEmpty())) { 280 nodeDesc.setText(Bundle.getMessage("NodeDesc") + " " + name); 281 nodeDesc.setVisible(true); 282 } 283 284 // initialize for input or output assignments 285 if (inputSelected) { 286 numBits = numInputBits; 287 assignmentPanel.setBorder(inputBorderTitled); 288 } else { 289 numBits = numOutputBits; 290 assignmentPanel.setBorder(outputBorderTitled); 291 } 292 ((AssignmentTableModel) assignmentListModel).fireTableDataChanged(); 293 } 294 295 /* Done button handler */ 296 public void doneButtonActionPerformed() { 297 setVisible(false); 298 dispose(); 299 } 300 301 /** 302 * Method to handle print button in List Frame. 303 * @param e unused. 304 */ 305 public void printButtonActionPerformed(java.awt.event.ActionEvent e) { 306 int[] colWidth = new int[5]; 307 // initialize column widths 308 TableColumnModel assignmentColumnModel = assignmentTable.getColumnModel(); 309 colWidth[0] = assignmentColumnModel.getColumn(AssignmentTableModel.BIT_COLUMN).getWidth(); 310 colWidth[1] = assignmentColumnModel.getColumn(AssignmentTableModel.ADDRESS_COLUMN).getWidth(); 311 colWidth[2] = assignmentColumnModel.getColumn(AssignmentTableModel.SYSNAME_COLUMN).getWidth(); 312 colWidth[3] = assignmentColumnModel.getColumn(AssignmentTableModel.USERNAME_COLUMN).getWidth(); 313 colWidth[4] = assignmentColumnModel.getColumn(AssignmentTableModel.COMMENT_COLUMN).getWidth(); 314 // set up a page title 315 String head; 316 if (inputSelected) { 317 head = Bundle.getMessage("Connection") +" "+ _memo.getUserName() + " "+ Bundle.getMessage("AssignmentPanelInputName") + " " 318 + Bundle.getMessage("NodeBoxLabel") + " " + selNodeNum + " "; 319 } else { 320 head = Bundle.getMessage("Connection") +" "+ _memo.getUserName() + " " + Bundle.getMessage("AssignmentPanelOutputName") + " " 321 + Bundle.getMessage("NodeBoxLabel") + " " + selNodeNum + " "; 322 } 323 // initialize a printer writer 324 CompatibleHardcopyWriter writer = null; 325 try { 326 writer = new CompatibleHardcopyWriter(curFrame, head, 10, .8, .5, .5, .5, false); 327 } catch (CompatibleHardcopyWriter.PrintCanceledException ex) { 328 //log.debug("Print cancelled"); 329 return; 330 } 331 writer.increaseLineSpacing(20); 332 // print the assignments 333 ((AssignmentTableModel) assignmentListModel).printTable(writer, colWidth); 334 } 335 336 /** 337 * Set up table for displaying bit assignments 338 */ 339 public class AssignmentTableModel extends AbstractTableModel { 340 341 private String free = Bundle.getMessage("AssignmentFree"); 342 private int curRow = -1; 343 private String curRowSysName = ""; 344 345 @Override 346 public String getColumnName(int c) { 347 return assignmentTableColumnNames[c]; 348 } 349 350 @Override 351 public Class<?> getColumnClass(int c) { 352 return String.class; 353 } 354 355 @Override 356 public boolean isCellEditable(int r, int c) { 357 return false; 358 } 359 360 @Override 361 public int getColumnCount() { 362 return MAX_COLS; 363 } 364 365 @Override 366 public int getRowCount() { 367 return numBits; 368 } 369 370 @Override 371 public Object getValueAt(int r, int c) { 372 if (c == BIT_COLUMN) { 373 return Integer.toString(r + 1); 374 } else if (c == ADDRESS_COLUMN) { 375 return Integer.toString((selNodeNum * 1000) + r + 1); 376 } else if (c == SYSNAME_COLUMN) { 377 String sName = null; 378 if (curRow != r) { 379 380 if (inputSelected) { 381 sName = _memo.isInputBitFree(selNodeNum, (r + 1)); 382 } else { 383 sName = _memo.isOutputBitFree(selNodeNum, (r + 1)); 384 } 385 curRow = r; 386 curRowSysName = sName; 387 } else { 388 sName = curRowSysName; 389 } 390 if (sName == null) { 391 return (free); 392 } else { 393 return sName; 394 } 395 } else if (c == USERNAME_COLUMN) { 396 String sName = null; 397 if (curRow != r) { 398 399 if (inputSelected) { 400 sName = _memo.isInputBitFree(selNodeNum, (r + 1)); 401 } else { 402 sName = _memo.isOutputBitFree(selNodeNum, (r + 1)); 403 } 404 405 curRow = r; 406 curRowSysName = sName; 407 } else { 408 sName = curRowSysName; 409 } 410 if (sName == null) { 411 return (""); 412 } else { 413 return (_memo.getUserNameFromSystemName(sName)); 414 } 415 } else if (c == COMMENT_COLUMN) { 416 String sName = null; 417 if (curRow != r) { 418 if (inputSelected) { 419 sName = _memo.isInputBitFree(selNodeNum, (r + 1)); 420 } else { 421 sName = _memo.isOutputBitFree(selNodeNum, (r + 1)); 422 } 423 curRow = r; 424 curRowSysName = sName; 425 } else { 426 sName = curRowSysName; 427 } 428 429 if (sName == null) { 430 return (""); 431 } 432 433 if (inputSelected) { 434 jmri.Sensor s = null; 435 s = jmri.InstanceManager.sensorManagerInstance().getBySystemName(sName); 436 if (s != null) { 437 return s.getComment(); 438 } 439 } else { 440 jmri.Turnout t = null; 441 t = jmri.InstanceManager.turnoutManagerInstance().getBySystemName(sName); 442 if (t != null) { 443 return t.getComment(); 444 } 445 } 446 447 } 448 449 return ""; // fall through 450 } 451 452 @Override 453 public void setValueAt(Object type, int r, int c) { 454 // nothing is stored here 455 } 456 457 public static final int BIT_COLUMN = 0; 458 public static final int ADDRESS_COLUMN = 1; 459 public static final int SYSNAME_COLUMN = 2; 460 public static final int USERNAME_COLUMN = 3; 461 public static final int COMMENT_COLUMN = 4; 462 public static final int MAX_COLS = COMMENT_COLUMN + 1; 463 464 /** 465 * Method to print or print preview the assignment table. Printed in 466 * proportionately sized columns across the page with headings and 467 * vertical lines between each column. Data is word wrapped within a 468 * column. Can only handle 4 columns of data as strings. Adapted from 469 * routines in BeanTableDataModel.java by Bob Jacobsen and Dennis Miller 470 * @param w hard copy writer instance. 471 * @param colWidth column width array. 472 */ 473 public void printTable(CompatibleHardcopyWriter w, int colWidth[]) { 474 // determine the column sizes - proportionately sized, with space between for lines 475 int[] columnSize = new int[MAX_COLS]; 476 int charPerLine = w.getCharactersPerLine(); 477 int tableLineWidth = 0; // table line width in characters 478 int totalColWidth = 0; 479 for (int j = 0; j < MAX_COLS; j++) { 480 totalColWidth += colWidth[j]; 481 } 482 float ratio = ((float) charPerLine) / ((float) totalColWidth); 483 for (int j = 0; j < MAX_COLS; j++) { 484 columnSize[j] = ((int) (colWidth[j] * ratio)) - 1; 485 tableLineWidth += (columnSize[j] + 1); 486 } 487 488 // Draw horizontal dividing line 489 w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), 490 tableLineWidth); 491 492 // print the column header labels 493 String[] columnStrings = new String[MAX_COLS]; 494 // Put each column header in the array 495 for (int i = 0; i < MAX_COLS; i++) { 496 columnStrings[i] = this.getColumnName(i); 497 } 498 //w.setFontStyle(Font.BOLD); 499 printColumns(w, columnStrings, columnSize); 500 w.setFontStyle(Font.PLAIN); 501 // draw horizontal line 502 w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), 503 tableLineWidth); 504 505 // now print each row of data 506 String[] spaces = new String[MAX_COLS]; 507 // create base strings the width of each of the columns 508 for (int k = 0; k < MAX_COLS; k++) { 509 spaces[k] = ""; 510 for (int i = 0; i < columnSize[k]; i++) { 511 spaces[k] = spaces[k] + " "; 512 } 513 } 514 for (int i = 0; i < this.getRowCount(); i++) { 515 for (int j = 0; j < MAX_COLS; j++) { 516 //check for special, null contents 517 if (this.getValueAt(i, j) == null) { 518 columnStrings[j] = spaces[j]; 519 } else { 520 columnStrings[j] = (String) this.getValueAt(i, j); 521 } 522 } 523 printColumns(w, columnStrings, columnSize); 524 // draw horizontal line 525 w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), 526 tableLineWidth); 527 } 528 w.close(); 529 } 530 531 protected void printColumns(CompatibleHardcopyWriter w, String columnStrings[], int columnSize[]) { 532 String columnString = ""; 533 StringBuilder lineString = new StringBuilder(""); 534 String[] spaces = new String[MAX_COLS]; 535 // create base strings the width of each of the columns 536 for (int k = 0; k < MAX_COLS; k++) { 537 spaces[k] = ""; 538 for (int i = 0; i < columnSize[k]; i++) { 539 spaces[k] = spaces[k] + " "; 540 } 541 } 542 // loop through each column 543 boolean complete = false; 544 while (!complete) { 545 complete = true; 546 for (int i = 0; i < MAX_COLS; i++) { 547 // if the column string is too wide cut it at word boundary (valid delimiters are space, - and _) 548 // use the initial part of the text,pad it with spaces and place the remainder back in the array 549 // for further processing on next line 550 // if column string isn't too wide, pad it to column width with spaces if needed 551 if (columnStrings[i].length() > columnSize[i]) { 552 // this column string will not fit on one line 553 boolean noWord = true; 554 for (int k = columnSize[i]; k >= 1; k--) { 555 if (columnStrings[i].substring(k - 1, k).equals(" ") 556 || columnStrings[i].substring(k - 1, k).equals("-") 557 || columnStrings[i].substring(k - 1, k).equals("_")) { 558 columnString = columnStrings[i].substring(0, k) 559 + spaces[i].substring(columnStrings[i].substring(0, k).length()); 560 columnStrings[i] = columnStrings[i].substring(k); 561 noWord = false; 562 complete = false; 563 break; 564 } 565 } 566 if (noWord) { 567 columnString = columnStrings[i].substring(0, columnSize[i]); 568 columnStrings[i] = columnStrings[i].substring(columnSize[i]); 569 complete = false; 570 } 571 } else { 572 // this column string will fit on one line 573 columnString = columnStrings[i] + spaces[i].substring(columnStrings[i].length()); 574 columnStrings[i] = ""; 575 } 576 lineString.append(columnString).append(" "); 577 } 578 try { 579 w.write(lineString.toString()); 580 //write vertical dividing lines 581 int iLine = w.getCurrentLineNumber(); 582 for (int i = 0, k = 0; i < w.getCharactersPerLine(); k++) { 583 w.write(iLine, i, iLine + 1, i); 584 if (k < MAX_COLS) { 585 i = i + columnSize[k] + 1; 586 } else { 587 i = w.getCharactersPerLine(); 588 } 589 } 590 w.write("\n"); 591 lineString = new StringBuilder(""); 592 } catch (IOException e) { 593 log.warn("error during printing", e); 594 } 595 } 596 } 597 } 598 private String[] assignmentTableColumnNames = {Bundle.getMessage("HeadingBit"), 599 Bundle.getMessage("HeadingAddress"), 600 Bundle.getMessage("HeadingSystemName"), 601 Bundle.getMessage("HeadingUserName"), 602 Bundle.getMessage("HeadingComment")}; 603 604 private static final Logger log = LoggerFactory.getLogger(NodeIOListFrame.class); 605 606} 607