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