001package jmri.jmrit.symbolicprog;
002
003import java.awt.Font;
004import java.awt.event.ActionEvent;
005import java.io.IOException;
006import java.util.Locale;
007
008import javax.swing.AbstractAction;
009import javax.swing.ImageIcon;
010import javax.swing.JLabel;
011import jmri.jmrit.roster.RosterEntry;
012import jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame;
013import jmri.util.FileUtil;
014import jmri.util.davidflanagan.HardcopyWriter;
015
016/**
017 * Action to print the information in the CV table.
018 * <p>
019 * This uses the older style printing, for compatibility with Java 1.1.8 in
020 * Macintosh MRJ
021 *
022 * @author Bob Jacobsen Copyright (C) 2003; D Miller Copyright 2003, 2005
023 */
024public class PrintCvAction extends AbstractAction {
025
026    static final int TABLE_COLS = 3;
027
028    public PrintCvAction(String actionName, CvTableModel pModel,
029            PaneProgFrame pParent, boolean preview, RosterEntry pRoster) {
030        super(actionName);
031        mModel = pModel;
032        mFrame = pParent;
033        isPreview = preview;
034        mRoster = pRoster;
035    }
036
037    /**
038     * Frame hosting the printing
039     */
040    private final PaneProgFrame mFrame;
041    private final CvTableModel mModel;
042    private final RosterEntry mRoster;
043    /**
044     * Variable to set whether this is to be printed or previewed
045     */
046    private final boolean isPreview;
047
048    public void printInfoSection(HardcopyWriter w) {
049        ImageIcon icon = new ImageIcon(FileUtil.findURL("resources/decoderpro.gif", FileUtil.Location.INSTALLED));
050        // we use an ImageIcon because it's guaranteed to have been loaded when ctor is complete
051        w.write(icon.getImage(), new JLabel(icon));
052        w.setFontStyle(Font.BOLD);
053        //Add a number of blank lines
054        int height = icon.getImage().getHeight(null);
055        int blanks = (height - w.getLineAscent()) / w.getLineHeight();
056
057        try {
058            for (int i = 0; i < blanks; i++) {
059                String s = "\n";
060                w.write(s, 0, s.length());
061            }
062        } catch (IOException e) {
063            log.warn("error during printing", e);
064        }
065        mRoster.printEntry(w);
066        w.setFontStyle(Font.PLAIN);
067    }
068
069    @Override
070    public void actionPerformed(ActionEvent e) {
071
072        // obtain a HardcopyWriter to do this
073        HardcopyWriter writer;
074        try {
075            writer = new HardcopyWriter(mFrame, mFrame.getRosterEntry().getId(), 10, .8, .5, .5, .5, isPreview);
076
077            // print the decoder info section, etc
078            printInfoSection(writer);
079            String s = "\n\n";
080            writer.write(s, 0, s.length());
081
082            //Initialize some variables to define the CV table size
083            int cvCount = mModel.getRowCount();
084            int tableLeft = 1;
085            int tableRight = TABLE_COLS * 24 + 1;
086            int tableTopRow;
087            int tableBottomRow;
088            int tableHeight = cvCount / TABLE_COLS;
089            if (cvCount % TABLE_COLS > 0) {
090                tableHeight++;
091            }
092
093            /*Start drawing the table of CVs. Set up the table with 4 columns of CV/Value
094             pairs and Draw the table borders and lines.  Each column width is
095             16 characters, including the starting vertical line, but not the
096             ending one.  Therefore the total table width is 64+1 characters
097             The colummn headings take 2 lines
098             4 columns of 20 gives 80 CVs possible. NMRA specs only define about 70 CVs
099             including all the optional ones plus some Manufacturer ones.  80 should be
100             enough, although more can be added by increasing the tableHeight value
101             */
102            //Set the top row and draw top line to start the table of CVs
103            tableTopRow = writer.getCurrentLineNumber();
104            writer.write(tableTopRow, tableLeft, tableTopRow, tableRight);
105
106            //set the bottom of the table
107            tableBottomRow = tableTopRow + tableHeight + 2;
108
109            //Draw vertical lines for columns
110            for (int i = 1; i < 76; i += 24) {
111                writer.write(tableTopRow, i, tableBottomRow, i);
112            }
113
114            //Draw remaining horozontal lines
115            writer.write(tableTopRow + 2, tableLeft, tableTopRow + 2, tableRight);
116            writer.write(tableBottomRow, tableLeft, tableBottomRow, tableRight);
117
118            writer.setFontStyle(1);  //set font to Bold
119            // print a simple heading with I18N
120            // pad with spaces to column width, 3 x insert Value as var %1
121            s = String.format("%1$21s%1$24s%1$24s", Bundle.getMessage("Value"));
122            writer.write(s, 0, s.length());
123            s = "\n";
124            writer.write(s, 0, s.length());
125            // NOI18N
126            s = "            CV  Dec Hex             CV  Dec Hex             CV  Dec Hex\n";
127            writer.write(s, 0, s.length());
128            writer.setFontStyle(0); //set font back to Normal
129
130            /* Create array to hold CV/Value strings to allow reformatting and sorting.
131             * Same size as the table drawn above (4 columns*tableHeight; heading rows
132             * not included
133             */
134            String[] cvStrings = new String[TABLE_COLS * tableHeight];
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())
172                            < cvSortOrderVal(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 < tableHeight; i++) {
183                s = cvStrings[i] + cvStrings[i + tableHeight] + cvStrings[i + tableHeight * 2] + "\n";
184                writer.write(s, 0, s.length());
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     * @param cvName cv name string to parse.
207     * @return the sort order value.
208     */
209    public static long cvSortOrderVal(String cvName) {
210        final int MAX_CVMNUM_SPACE = 1200;
211
212        // Split the string by any non-numeric character
213        String[] cvNumStrings = cvName.split("\\D+");
214        long sortVal = 0;
215        for (int i = 0; i < (cvNumStrings.length); i++) {
216            sortVal = (sortVal * MAX_CVMNUM_SPACE) + Integer.parseInt(cvNumStrings[i]);
217        }
218        return sortVal;
219    }
220
221    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PrintCvAction.class);
222
223}