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