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}