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}