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