001package jmri.jmrix.can.cbus.swing.eventtable;
002
003import java.awt.Font;
004import java.awt.Frame;
005import java.awt.event.ActionEvent;
006import java.io.IOException;
007
008import javax.annotation.Nonnull;
009import javax.swing.AbstractAction;
010import jmri.jmrix.can.cbus.eventtable.CbusEventTableDataModel;
011import jmri.util.davidflanagan.OriginalHardcopyWriter;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * Print or Print Preview Action for CBUS Event Table
018 */
019public class CbusEventTablePrintAction extends AbstractAction {
020
021    private final static int[] whichPrintColumns = {CbusEventTableDataModel.NODE_COLUMN,
022        CbusEventTableDataModel.EVENT_COLUMN,CbusEventTableDataModel.NAME_COLUMN,
023        CbusEventTableDataModel.NODENAME_COLUMN,CbusEventTableDataModel.COMMENT_COLUMN};
024
025    private final String _title;
026    private final CbusEventTableDataModel _model;
027    private final boolean _preview;
028
029    /**
030     * Create a new Save to CSV Action.
031     *
032     * @param actionName Action Name
033     * @param model Table Model to use.
034     * @param title Page Title.
035     * @param preview True to preview, false to print.
036     */
037    public CbusEventTablePrintAction(String actionName, @Nonnull CbusEventTableDataModel model,
038        @Nonnull String title, boolean preview ){
039        super(actionName);
040        _model = model;
041        _title = title;
042        _preview = preview;
043
044    }
045
046    /**
047     * {@inheritDoc}
048     */
049    @Override
050    public void actionPerformed(ActionEvent e) {
051
052        jmri.util.ThreadingUtil.runOnGUIEventually( () -> {
053            OriginalHardcopyWriter writer;
054            try {
055                writer = new OriginalHardcopyWriter(new Frame(), _title, 10, .8, .5, .5, .5, _preview);
056            } catch (OriginalHardcopyWriter.PrintCanceledException ex) {
057                // log.debug("Preview cancelled");
058                return;
059            }
060            writer.increaseLineSpacing(20);
061            printTable(writer); // close() is taken care of in printTable()
062            writer.close();
063        });
064    }
065
066    /**
067     * Self print or print preview the table.
068     * <p>
069     * Copied from BeanTableDataModel modified to print variable column widths.
070     * Final column with size zero runs to extent of page width.
071     * <p>
072     * Printed with headings and vertical lines between each column. Data is
073     * word wrapped within a column.
074     *
075     * @param w the writer to print to
076     */
077    private void printTable(OriginalHardcopyWriter w ) {
078
079        // [AC] variable column sizes
080
081        // column header labels
082        String[] columnStrings = new String[whichPrintColumns.length];
083
084        int[] columnWidth = new int[whichPrintColumns.length];
085        // in a test, thats 86 chars on a line
086
087        colWidthLoop(columnStrings, columnWidth, w);
088
089        // Draw horizontal dividing line
090        w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(),
091                w.getCharactersPerLine());
092
093        w.setFontStyle(Font.BOLD);
094        printColumns(w, columnStrings, columnWidth);
095        w.setFontStyle(Font.PLAIN);
096        w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(),
097                w.getCharactersPerLine());
098
099        getEachRow(w, columnStrings, columnWidth);
100
101
102    }
103
104    private void colWidthLoop(String[] columnStrings, int[] columnWidth, OriginalHardcopyWriter w){
105        int columnTotal = 0;
106        for (int i = 0; i < whichPrintColumns.length; i++) {
107            // Put each column header in the array
108            columnStrings[i] = _model.getColumnName(whichPrintColumns[i]);
109
110            int columnworkedon=whichPrintColumns[i];
111
112            if (getColumnWidth(columnworkedon) == 0) {
113                // Fill to end of line
114                columnWidth[i] = w.getCharactersPerLine() - columnTotal;
115            } else {
116                columnWidth[i] = getColumnWidth(columnworkedon);
117                columnTotal = columnTotal + columnWidth[i] + 1;
118            }
119        }
120    }
121
122    private void getEachRow(OriginalHardcopyWriter w, String[] columnStrings, int[] columnWidth){
123
124        // now print each row of data
125        // create a base string the width of the column
126        for (int i = 0; i < _model.getRowCount(); i++) {
127            for (int k = 0; k < whichPrintColumns.length; k++) {
128
129                int j=whichPrintColumns[k];
130
131                //check for special, non string contents
132                if (_model.getValueAt(i, j) instanceof Integer) {
133                    columnStrings[k] = (_model.getValueAt(i, j)).toString();
134                } else {
135                    columnStrings[k] = (String) _model.getValueAt(i, j);
136                }
137            }
138
139            printColumns(w, columnStrings, columnWidth);
140            w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(),
141                    w.getCharactersPerLine());
142        }
143    }
144
145    // [AC] modified to take an array of column widths
146    private void printColumns(OriginalHardcopyWriter w, String columnStrings[], int columnWidth[]) {
147        String columnString = "";
148        StringBuilder lineString = new StringBuilder();
149        String spaces;
150        // loop through each column
151        boolean complete = false;
152        while (!complete) {
153            complete = true;
154            for (int i = 0; i < columnStrings.length; i++) {
155                // create a base string the width of the column
156                StringBuilder buf = new StringBuilder();
157                for (int j = 0; j < columnWidth[i]; j++) {
158                    buf.append(" ");
159                }
160                spaces = buf.toString();
161                // if the column string is too wide, cut it at word boundary (valid delimiters are space, - and _)
162                // Use the intial part of the text, pad it with spaces and place the remainder back in the array
163                // for further processing on next line.
164                // If column string isn't too wide, pad it to column width with spaces if needed
165                if (columnStrings[i].length() > columnWidth[i]) {
166                    boolean noWord = true;
167                    for (int k = columnWidth[i]; k >= 1; k--) {
168                        if (columnStrings[i].substring(k - 1, k).equals(" ")
169                                || columnStrings[i].substring(k - 1, k).equals("-")
170                                || columnStrings[i].substring(k - 1, k).equals("_")) {
171                            columnString = columnStrings[i].substring(0, k)
172                                    + spaces.substring(k);
173                            columnStrings[i] = columnStrings[i].substring(k);
174                            noWord = false;
175                            complete = false;
176                            break;
177                        }
178                        // log.debug("1050 columnString {}",columnString);
179                    }
180
181                    // log.debug("1053 noword is {} ",noWord);
182                    if (noWord) { // not breakable, hard break
183                        columnString = columnStrings[i].substring(0, columnWidth[i]);
184                        columnStrings[i] = columnStrings[i].substring(columnWidth[i]);
185                        complete = false;
186                    }
187                } else {
188                    columnString = columnStrings[i] + spaces.substring(columnStrings[i].length()); // pad with spaces
189                    columnStrings[i] = "";
190                }
191                lineString.append(columnString).append(" ");
192            }
193            try {
194                w.write(lineString.toString());
195                //write vertical dividing lines
196                int column = 0;
197                for (int i = 0; i < whichPrintColumns.length; i++) {
198                    w.write(w.getCurrentLineNumber(), column, w.getCurrentLineNumber() + 1, column);
199                    column = column + columnWidth[i] + 1;
200                    // log.debug("1167 i is {} column is {} columnWidth[i] is {} ", i, column, columnWidth[i]);
201                }
202                w.write(w.getCurrentLineNumber(), w.getCharactersPerLine(),
203                    w.getCurrentLineNumber() + 1, w.getCharactersPerLine());
204                w.write("\n");
205            } catch (IOException e) {
206                log.warn("error during printing", e);
207            }
208        }
209    }
210
211
212    /**
213     * Returns int of column width.
214     * <p>
215     * Just used for printing.
216     * in a test, there is 86 chars on a line
217     * -1 is invalid
218     * 0 is final column extend to end
219     *
220     * @param col int col number
221     * @return print width
222     */
223    private static int getColumnWidth(int col) {
224        switch (col) {
225            case CbusEventTableDataModel.NAME_COLUMN:
226                return 14;
227            case CbusEventTableDataModel.COMMENT_COLUMN:
228                return 0; // 0 to get writer recognize it as the last column, will fill with spaces
229            default:
230                return 8;
231        }
232    }
233
234    private final static Logger log = LoggerFactory.getLogger(CbusEventTablePrintAction.class);
235
236}