001package jmri.jmrit.symbolicprog;
002
003import java.awt.Font;
004import java.awt.event.ActionEvent;
005import java.util.Locale;
006
007import javax.swing.AbstractAction;
008
009import jmri.jmrit.roster.RosterEntry;
010import jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame;
011import jmri.util.davidflanagan.HardcopyWriter;
012
013/**
014 * Action to print the information in the CV table.
015 * <p>
016 * This uses the older style printing, for compatibility with Java 1.1.8 in
017 * Macintosh MRJ
018 *
019 * @author Bob Jacobsen Copyright (C) 2003; D Miller Copyright 2003, 2005
020 */
021public class PrintCvAction extends AbstractAction {
022
023    static final int TABLE_COLS = 3;
024
025    public PrintCvAction(String actionName, CvTableModel pModel,
026            PaneProgFrame pParent, boolean preview, RosterEntry pRoster) {
027        super(actionName);
028        mModel = pModel;
029        mFrame = pParent;
030        isPreview = preview;
031        mRoster = pRoster;
032    }
033
034    /**
035     * Frame hosting the printing
036     */
037    private final PaneProgFrame mFrame;
038    private final CvTableModel mModel;
039    private final RosterEntry mRoster;
040    /**
041     * Variable to set whether this is to be printed or previewed
042     */
043    private final boolean isPreview;
044
045    public void printInfoSection(HardcopyWriter w) {
046        // Write out the icon
047        w.writeDecoderProIcon();
048        w.setFont(null, Font.BOLD, null);
049
050        mRoster.printEntry(w);
051        w.setFont(null, Font.PLAIN, null);
052    }
053
054    @Override
055    public void actionPerformed(ActionEvent e) {
056
057        // obtain a HardcopyWriter to do this
058        HardcopyWriter writer;
059        try {
060            writer = new HardcopyWriter(mFrame, mFrame.getRosterEntry().getId(), null, null, 10,
061                    .8 * 72, .5 * 72, .5 * 72, .5 * 72, isPreview, null, null, null, null, null);
062
063            // print the decoder info section, etc
064            printInfoSection(writer);
065            String s = "\n\n";
066            writer.write(s);
067
068            //Initialize some variables to define the CV table size
069            int cvCount = mModel.getRowCount();
070            int tableLeft = 1;
071            int tableRight = TABLE_COLS * 24 + 1;   // ISSUE: this is wrong
072            int tableTopPos;
073            int tableBottomPos;
074            int tableHeightPoints = (cvCount / TABLE_COLS) * writer.getLineHeight();  
075            if (cvCount % TABLE_COLS > 0) {
076                tableHeightPoints += writer.getLineHeight();
077            }
078
079            int tableHeightRows = tableHeightPoints / writer.getLineHeight();
080
081            /*
082             * Start drawing the table of CVs. Set up the table with 4 columns
083             * of CV/Value pairs and Draw the table borders and lines. Each
084             * column width is 16 characters, including the starting vertical
085             * line, but not the ending one. Therefore the total table width is
086             * 64+1 characters The colummn headings take 2 lines 4 columns of 20
087             * gives 80 CVs possible. NMRA specs only define about 70 CVs
088             * including all the optional ones plus some Manufacturer ones. 80
089             * should be enough, although more can be added by increasing the
090             * tableHeight value
091             */
092
093            /* 
094             * ISSUE: this is wrong as it doesn't take into account what happens when the table is printed on multiple pages. 
095             * The table is drawn as if it were on a single page, but it may not be. In particular the vertical lines only
096             * appear on the first page, and *maybe* we ought to put the column headings on the subsequent pages too.
097            */
098            //Set the top row and draw top line to start the table of CVs
099            tableTopPos = writer.getCurrentVPos();
100
101            //set the bottom of the table
102            tableBottomPos = tableTopPos + tableHeightPoints + 2;
103
104            float useCharWidth = writer.getCharWidth();
105
106            writer.writeLine(tableTopPos, (int) (tableLeft * useCharWidth), tableTopPos, (int) (tableRight * useCharWidth));
107
108            //Draw vertical lines for columns
109            for (int i = 1; i < 76; i += 24) {
110                writer.writeLine(tableTopPos, (int) (i * useCharWidth), tableBottomPos, (int) (i * useCharWidth));    // ISSUE: this is wrong
111            }
112
113            //Draw remaining horozontal lines
114            writer.writeLine(tableTopPos + 2 * writer.getLineHeight(), (int) (tableLeft * useCharWidth), tableTopPos + 2 * writer.getLineHeight(), (int) (tableRight * useCharWidth));
115            writer.writeLine(tableBottomPos, (int) (tableLeft * useCharWidth), tableBottomPos, (int) (tableRight * useCharWidth));
116
117            writer.setFont(null, Font.BOLD, null); //set font to Bold
118            // print a simple heading with I18N
119            // pad with spaces to column width, 3 x insert Value as var %1
120            s = String.format("%1$21s%1$24s%1$24s", Bundle.getMessage("Value"));
121            writer.write(s);
122            s = "\n";
123            writer.write(s);
124            // NOI18N
125            s = "            CV  Dec Hex             CV  Dec Hex             CV  Dec Hex\n";
126            writer.write(s);
127            writer.setFont(null, Font.PLAIN, null); //set font back to Normal
128
129            /*
130             * Create array to hold CV/Value strings to allow reformatting and
131             * sorting. Same size as the table drawn above (4
132             * columns*tableHeight; heading rows not included
133             */
134            String[] cvStrings = new String[TABLE_COLS * tableHeightRows];
135
136            //blank the array
137            for (int i = 0; i < cvStrings.length; i++) {
138                cvStrings[i] = "";
139            }
140
141            // get each CV and value
142            for (int i = 0; i < mModel.getRowCount(); i++) {
143                CvValue cv = mModel.getCvByRow(i);
144                int value = cv.getValue();
145
146                //convert and pad numbers as needed
147                String numString = String.format("%12s", cv.number());
148                String valueString = Integer.toString(value);
149                String valueStringHex = Integer.toHexString(value).toUpperCase(Locale.ENGLISH);
150                if (value < 16) {
151                    valueStringHex = "0" + valueStringHex;
152                }
153                for (int j = 1; j < 3; j++) {
154                    if (valueString.length() < 3) {
155                        valueString = " " + valueString;
156                    }
157                }
158                //Create composite string of CV and its decimal and hex values
159                s = "  " + numString + "  " + valueString + "  " + valueStringHex + " ";
160
161                //populate printing array - still treated as a single column
162                cvStrings[i] = s;
163            }
164
165            //sort the array in CV order (just the members with values)
166            String temp;
167            boolean swap;
168            do {
169                swap = false;
170                for (int i = 0; i < mModel.getRowCount() - 1; i++) {
171                    if (cvSortOrderVal(cvStrings[i + 1].substring(0, 15).trim()) < cvSortOrderVal(
172                            cvStrings[i].substring(0, 15).trim())) {
173                        temp = cvStrings[i + 1];
174                        cvStrings[i + 1] = cvStrings[i];
175                        cvStrings[i] = temp;
176                        swap = true;
177                    }
178                }
179            } while (swap);
180
181            //Print the array in three columns
182            for (int i = 0; i < tableHeightRows; i++) {
183                s = cvStrings[i] + cvStrings[i + tableHeightRows] + cvStrings[i + tableHeightRows * 2] + "\n";
184                writer.write(s);
185            }
186            //write an extra character to work around the
187            //last character truncation bug with HardcopyWriter
188            //s = " \n";
189            //writer.write(s, 0, s.length());
190        } catch (java.io.IOException ex1) {
191            log.error("IO exception while printing");
192            return;
193        } catch (HardcopyWriter.PrintCanceledException ex2) {
194            log.debug("Print cancelled");
195            return;
196        }
197
198        writer.close();
199    }
200
201    /**
202     * Returns a representation of a CV name as a long integer sort order value.
203     * <p>
204     * The value itself is not meaningful, but is used in comparisons when
205     * sorting.
206     * 
207     * @param cvName cv name string to parse.
208     * @return the sort order value.
209     */
210    public static long cvSortOrderVal(String cvName) {
211        final int MAX_CVMNUM_SPACE = 1200;
212
213        // Split the string by any non-numeric character
214        String[] cvNumStrings = cvName.split("\\D+");
215        long sortVal = 0;
216        for (int i = 0; i < (cvNumStrings.length); i++) {
217            sortVal = (sortVal * MAX_CVMNUM_SPACE) + Integer.parseInt(cvNumStrings[i]);
218        }
219        return sortVal;
220    }
221
222    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PrintCvAction.class);
223
224}