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