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