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.CompatibleHardcopyWriter; 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 } else if (type == SerialNode.ESP32NODE) { 291 int bitsPerCard = selNode.getNumBitsPerCard(); 292 int numInputCards = selNode.numInputCards(); 293 int numOutputCards = selNode.numOutputCards(); 294 numInputBits = bitsPerCard * numInputCards; 295 numOutputBits = bitsPerCard * numOutputCards; 296 nodeInfoText.setText("ESP32Node - " + bitsPerCard + " " + Bundle.getMessage("BitsPerCard") 297 + ", " + numInputBits + " " + Bundle.getMessage("InputBitsAnd") + " " 298 + numOutputBits + " " + Bundle.getMessage("OutputBits")); 299 } 300 301// here insert code for new types of C/MRI nodes 302 } 303 // initialize for input or output assignments 304 if (inputSelected) { 305 numBits = numInputBits; 306 assignmentPanel.setBorder(inputBorderTitled); 307 } else { 308 numBits = numOutputBits; 309 assignmentPanel.setBorder(outputBorderTitled); 310 } 311 ((AssignmentTableModel) assignmentListModel).fireTableDataChanged(); 312 } 313 314 /** 315 * Handle print button in List Frame. 316 * @param e unused. 317 */ 318 public void printButtonActionPerformed(java.awt.event.ActionEvent e) { 319 int[] colWidth = new int[AssignmentTableModel.MAX_COLS]; 320 // initialize column widths 321 TableColumnModel assignmentColumnModel = assignmentTable.getColumnModel(); 322 colWidth[0] = assignmentColumnModel.getColumn(AssignmentTableModel.BIT_COLUMN).getWidth(); 323 colWidth[1] = assignmentColumnModel.getColumn(AssignmentTableModel.ADDRESS_COLUMN).getWidth(); 324 colWidth[2] = assignmentColumnModel.getColumn(AssignmentTableModel.SYSNAME_COLUMN).getWidth(); 325 colWidth[3] = assignmentColumnModel.getColumn(AssignmentTableModel.USERNAME_COLUMN).getWidth(); 326 colWidth[4] = assignmentColumnModel.getColumn(AssignmentTableModel.COMMENT_COLUMN).getWidth(); 327 328 // set up a page title 329 String head; 330 if (inputSelected) { 331 head = Bundle.getMessage("Connection") +" "+ _memo.getUserName() + " "+ Bundle.getMessage("AssignmentPanelInputName") + " " 332 + Bundle.getMessage("NodeBoxLabel") + " " + selNodeID + " "; 333 } else { 334 head = Bundle.getMessage("Connection") +" "+ _memo.getUserName() + " " + Bundle.getMessage("AssignmentPanelOutputName") + " " 335 + Bundle.getMessage("NodeBoxLabel") + " " + selNodeID + " "; 336 } 337 // initialize a printer writer 338 CompatibleHardcopyWriter writer = null; 339 try { 340 writer = new CompatibleHardcopyWriter(curFrame, head, 10, .8, .5, .5, .5, false); 341 } catch (CompatibleHardcopyWriter.PrintCanceledException ex) { 342 //log.debug("Print cancelled"); 343 return; 344 } 345 writer.increaseLineSpacing(20); 346 // print the assignments 347 ((AssignmentTableModel) assignmentListModel).printTable(writer, colWidth); 348 } 349 350 /** 351 * Set up table for displaying bit assignments 352 */ 353 public class AssignmentTableModel extends AbstractTableModel { 354 355 private String free = Bundle.getMessage("AssignmentFree"); 356 private int curRow = -1; 357 private String curRowSysName = ""; 358 359 @Override 360 public String getColumnName(int c) { 361 return assignmentTableColumnNames[c]; 362 } 363 364 @Override 365 public Class<?> getColumnClass(int c) { 366 return String.class; 367 } 368 369 @Override 370 public boolean isCellEditable(int r, int c) { 371 return false; 372 } 373 374 @Override 375 public int getColumnCount() { 376 return MAX_COLS; 377 } 378 379 @Override 380 public int getRowCount() { 381 return numBits; 382 } 383 384 @Override 385 public Object getValueAt(int r, int c) { 386 if (c == BIT_COLUMN) { 387 return Integer.toString(r + 1); 388 } else if (c == ADDRESS_COLUMN) { 389 return Integer.toString((selNodeNum * 1000) + r + 1); 390 } else if (c == SYSNAME_COLUMN) { 391 String sName = null; 392 if (curRow != r) { 393 if (inputSelected) { 394 sName = _memo.isInputBitFree(selNodeNum, (r + 1)); 395 } else { 396 sName = _memo.isOutputBitFree(selNodeNum, (r + 1)); 397 } 398 curRow = r; 399 curRowSysName = sName; 400 } else { 401 sName = curRowSysName; 402 } 403 if (sName == null) { 404 return (free); 405 } else { 406 return sName; 407 } 408 409 } else if (c == USERNAME_COLUMN) { 410 String sName = null; 411 if (curRow != r) { 412 if (inputSelected) { 413 sName = _memo.isInputBitFree(selNodeNum, (r + 1)); 414 } else { 415 sName = _memo.isOutputBitFree(selNodeNum, (r + 1)); 416 } 417 curRow = r; 418 curRowSysName = sName; 419 } else { 420 sName = curRowSysName; 421 } 422 if (sName == null) { 423 return (""); 424 } else { 425 return (_memo.getUserNameFromSystemName(sName)); 426 } 427 428 429 } else if (c == COMMENT_COLUMN) { 430 String sName = null; 431 if (curRow != r) { 432 if (inputSelected) { 433 sName = _memo.isInputBitFree(selNodeNum, (r + 1)); 434 } else { 435 sName = _memo.isOutputBitFree(selNodeNum, (r + 1)); 436 } 437 curRow = r; 438 curRowSysName = sName; 439 } else { 440 sName = curRowSysName; 441 } 442 if (sName == null) { 443 return (""); 444 } 445 446 if (inputSelected) { 447 jmri.Sensor s = null; 448 s = jmri.InstanceManager.sensorManagerInstance().getBySystemName(sName); 449 if (s != null) { 450 return s.getComment(); 451 } 452 } else { 453 jmri.Turnout t = null; 454 t = jmri.InstanceManager.turnoutManagerInstance().getBySystemName(sName); 455 if (t != null) { 456 return t.getComment(); 457 } 458 } 459 460 } 461 462 return ""; 463 } 464 465 @Override 466 public void setValueAt(Object type, int r, int c) { 467 // nothing is stored here 468 } 469 470 public static final int BIT_COLUMN = 0; 471 public static final int ADDRESS_COLUMN = 1; 472 public static final int SYSNAME_COLUMN = 2; 473 public static final int USERNAME_COLUMN = 3; 474 public static final int COMMENT_COLUMN = 4; 475 public static final int MAX_COLS = COMMENT_COLUMN + 1; 476 477 /** 478 * Print or print preview the assignment table. Printed in 479 * proportionately sized columns across the page with headings and 480 * vertical lines between each column. Data is word wrapped within a 481 * column. Can only handle 4 columns of data as strings. Adapted from 482 * routines in BeanTableDataModel.java by Bob Jacobsen and Dennis Miller 483 * @param w hard copy writer instance. 484 * @param colWidth column width array. 485 */ 486 public void printTable(CompatibleHardcopyWriter w, int colWidth[]) { 487 // determine the column sizes - proportionately sized, with space between for lines 488 int[] columnSize = new int[MAX_COLS]; 489 int charPerLine = w.getCharactersPerLine(); 490 int tableLineWidth = 0; // table line width in characters 491 int totalColWidth = 0; 492 for (int j = 0; j < MAX_COLS; j++) { 493 totalColWidth += colWidth[j]; 494 } 495 float ratio = ((float) charPerLine) / ((float) totalColWidth); 496 for (int j = 0; j < MAX_COLS; j++) { 497 columnSize[j] = ((int) (colWidth[j] * ratio)) - 1; 498 tableLineWidth += (columnSize[j] + 1); 499 } 500 501 // Draw horizontal dividing line 502 w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), 503 tableLineWidth); 504 505 // print the column header labels 506 String[] columnStrings = new String[AssignmentTableModel.MAX_COLS]; 507 // Put each column header in the array 508 for (int i = 0; i < MAX_COLS; i++) { 509 columnStrings[i] = this.getColumnName(i); 510 } 511 w.setFontStyle(Font.BOLD); 512 printColumns(w, columnStrings, columnSize); 513 w.setFontStyle(Font.PLAIN); 514 // draw horizontal line 515 w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), 516 tableLineWidth); 517 518 // now print each row of data 519 String[] spaces = new String[MAX_COLS]; 520 // create base strings the width of each of the columns 521 for (int k = 0; k < MAX_COLS; k++) { 522 spaces[k] = ""; 523 for (int i = 0; i < columnSize[k]; i++) { 524 spaces[k] = spaces[k] + " "; 525 } 526 } 527 for (int i = 0; i < this.getRowCount(); i++) { 528 for (int j = 0; j < MAX_COLS; j++) { 529 //check for special, null contents 530 if (this.getValueAt(i, j) == null) { 531 columnStrings[j] = spaces[j]; 532 } else { 533 columnStrings[j] = (String) this.getValueAt(i, j); 534 } 535 } 536 printColumns(w, columnStrings, columnSize); 537 // draw horizontal line 538 w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), 539 tableLineWidth); 540 } 541 w.close(); 542 } 543 544 protected void printColumns(CompatibleHardcopyWriter w, String columnStrings[], int columnSize[]) { 545 String columnString = ""; 546 StringBuilder lineString = new StringBuilder(); 547 StringBuilder[] spaces = new StringBuilder[MAX_COLS]; 548 // create base strings the width of each of the columns 549 for (int k = 0; k < MAX_COLS; k++) { 550 spaces[k] = new StringBuilder(); 551 for (int i = 0; i < columnSize[k]; i++) { 552 spaces[k].append(" "); 553 } 554 } 555 // loop through each column 556 boolean complete = false; 557 while (!complete) { 558 complete = true; 559 for (int i = 0; i < MAX_COLS; i++) { 560 // if the column string is too wide cut it at word boundary (valid delimiters are space, - and _) 561 // use the initial part of the text,pad it with spaces and place the remainder back in the array 562 // for further processing on next line 563 // if column string isn't too wide, pad it to column width with spaces if needed 564 if (columnStrings[i].length() > columnSize[i]) { 565 // this column string will not fit on one line 566 boolean noWord = true; 567 for (int k = columnSize[i]; k >= 1; k--) { 568 if (columnStrings[i].substring(k - 1, k).equals(" ") 569 || columnStrings[i].substring(k - 1, k).equals("-") 570 || columnStrings[i].substring(k - 1, k).equals("_")) { 571 columnString = columnStrings[i].substring(0, k) 572 + spaces[i].substring(columnStrings[i].substring(0, k).length()); 573 columnStrings[i] = columnStrings[i].substring(k); 574 noWord = false; 575 complete = false; 576 break; 577 } 578 } 579 if (noWord) { 580 columnString = columnStrings[i].substring(0, columnSize[i]); 581 columnStrings[i] = columnStrings[i].substring(columnSize[i]); 582 complete = false; 583 } 584 } else { 585 // this column string will fit on one line 586 columnString = columnStrings[i] + spaces[i].substring(columnStrings[i].length()); 587 columnStrings[i] = ""; 588 } 589 lineString.append(columnString).append(" "); 590 } 591 try { 592 w.write(lineString.toString()); 593 //write vertical dividing lines 594 int iLine = w.getCurrentLineNumber(); 595 for (int i = 0, k = 0; i < w.getCharactersPerLine(); k++) { 596 w.write(iLine, i, iLine + 1, i); 597 if (k < MAX_COLS) { 598 i = i + columnSize[k] + 1; 599 } else { 600 i = w.getCharactersPerLine(); 601 } 602 } 603 w.write("\n"); 604 lineString = new StringBuilder(); 605 } catch (IOException e) { 606 log.warn("error during printing", e); 607 } 608 } 609 } 610 } 611 private String[] assignmentTableColumnNames = { 612 Bundle.getMessage("HeadingBit"), 613 Bundle.getMessage("HeadingAddress"), 614 Bundle.getMessage("HeadingSystemName"), 615 Bundle.getMessage("HeadingUserName"), 616 Bundle.getMessage("HeadingComment") 617 }; 618 619 private static final Logger log = LoggerFactory.getLogger(ListFrame.class); 620 621}