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