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