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