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