001package jmri.jmrit.roster;
002
003import java.awt.Font;
004import java.awt.Frame;
005import java.awt.event.ActionEvent;
006import java.awt.geom.Rectangle2D;
007import java.io.IOException;
008import java.util.ArrayList;
009import java.util.Arrays;
010import java.util.List;
011import jmri.beans.BeanUtil;
012import jmri.jmrit.roster.rostergroup.RosterGroupSelector;
013import jmri.jmrit.roster.swing.RosterFrame;
014import jmri.util.davidflanagan.HardcopyWriter;
015import org.slf4j.Logger;
016import org.slf4j.LoggerFactory;
017
018/**
019 * Action to print a very compact summary listing of the Roster contents.
020 * <p>
021 * This uses the older style printing, for compatibility with Java 1.1.8 in
022 * Macintosh MRJ
023 *
024 * @author Bob Jacobsen Copyright (C) 2003
025 * @author Dennis Miller Copyright (C) 2005
026 * @author Egbert Broerse Copyright (C) 2018
027 */
028public class PrintListAction extends jmri.util.swing.JmriAbstractAction {
029
030    public PrintListAction(String s, jmri.util.swing.WindowInterface wi) {
031        super(s, wi);
032        isPreview = true;
033    }
034
035    public PrintListAction(String s, javax.swing.Icon i, jmri.util.swing.WindowInterface wi) {
036        super(s, i, wi);
037        isPreview = true;
038    }
039
040    public PrintListAction(String actionName, Frame frame, boolean preview) {
041        super(actionName);
042        mFrame = frame;
043        isPreview = preview;
044    }
045
046    public void setPreview(boolean preview) {
047        isPreview = preview;
048    }
049
050    /**
051     * Frame hosting the printing.
052     */
053    Frame mFrame = new Frame();
054
055    /**
056     * Variable to set whether this is to be printed or previewed.
057     */
058    boolean isPreview;
059
060    @Override
061    public void actionPerformed(ActionEvent e) {
062        // obtain a HardcopyWriter to do this
063        Roster r = Roster.getDefault();
064        String title = Bundle.getMessage("TitleDecoderProRoster");
065        String rosterGroup = r.getDefaultRosterGroup();
066        // rosterGroup may legitimately be null
067        // but getProperty returns null if the property cannot be found, so
068        // we test that the property exists before attempting to get its value
069        if (BeanUtil.hasProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP)) {
070            rosterGroup = (String) BeanUtil.getProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP);
071        }
072        if (rosterGroup == null) {
073            title = title + " " + Bundle.getMessage("ALLENTRIES");
074        } else {
075            title = title +
076                    " " +
077                    Bundle.getMessage("TitleGroup") +
078                    " " +
079                    Bundle.getMessage("TitleEntries", rosterGroup);
080        }
081        try (HardcopyWriter writer = new HardcopyWriter(mFrame, title, "SansSerif", null, 9,
082                .5 * 72, .5 * 72, .5 * 72, .5 * 72, isPreview, null, null, null, null, null);) {
083
084            // add the icon
085            writer.writeDecoderProIcon();
086
087            // Loop through the Roster, printing a 1 line list entry as needed
088            List<RosterEntry> l;
089
090            if (BeanUtil.hasProperty(wi, "allRosterEntries")) {
091                l = Arrays.asList(((RosterFrame) wi).getAllRosterEntries());
092            } else {
093                l = r.matchingList(null, null, null, null, null, null, null); // take all
094            }
095            log.debug("Roster list size: {}", l.size());
096
097            // print table column headers, match column order + widths with RosterEntry#PrintEntryLine
098            // fields copied from RosterTableModel#getColumnName(int)
099            List<HardcopyWriter.Column> columns = new ArrayList<>();
100            columns.add(new HardcopyWriter.Column(0, 15, HardcopyWriter.Align.LEFT));  // ID
101            columns.add(new HardcopyWriter.Column(0, 5, HardcopyWriter.Align.RIGHT));  // DCC Address
102            columns.add(new HardcopyWriter.Column(0, 7, HardcopyWriter.Align.LEFT));   // Road name
103            columns.add(new HardcopyWriter.Column(0, 6, HardcopyWriter.Align.LEFT));   // Road number
104            columns.add(new HardcopyWriter.Column(0, 6, HardcopyWriter.Align.LEFT));   // Manufacturer
105            columns.add(new HardcopyWriter.Column(0, 10, HardcopyWriter.Align.LEFT));  // Model
106            columns.add(new HardcopyWriter.Column(0, 10, HardcopyWriter.Align.LEFT));  // Decoder model
107            columns.add(new HardcopyWriter.Column(0, 12, HardcopyWriter.Align.LEFT));  // Protocol
108            columns.add(new HardcopyWriter.Column(0, 6, HardcopyWriter.Align.LEFT));   // Owner
109            columns.add(new HardcopyWriter.Column(0, 10, HardcopyWriter.Align.LEFT));  // Date updated
110
111            columns = HardcopyWriter.Column.stretchColumns(columns, 
112                                                           (int) writer.getPrintablePagesizePoints().getWidth(), 
113                                                           writer.getFontSize() / 2);
114
115            // If the paper is very wide, we may need to reduce the width of some columns
116            // so to make the other columns a bit larger
117            List<String> dccAddress = new ArrayList<>();
118            List<String> protocols = new ArrayList<>();
119            for (RosterEntry re : l) {
120                dccAddress.add(re.getDccAddress());
121                protocols.add(re.getProtocol().toString());
122            }
123
124            Rectangle2D dccAddressBounds = writer.measure(dccAddress);
125            Rectangle2D protocolsBounds = writer.measure(protocols);
126
127            boolean changed = false;
128
129            if (Math.ceil(dccAddressBounds.getWidth()) < columns.get(1).getWidth()) {
130                columns.get(1).setWidth((int) Math.ceil(dccAddressBounds.getWidth()));
131                changed = true;
132            }
133            if (Math.ceil(protocolsBounds.getWidth()) < columns.get(7).getWidth()) {
134                columns.get(7).setWidth((int) Math.ceil(protocolsBounds.getWidth()));
135                changed = true;
136            }
137
138            if (changed) {
139                columns = HardcopyWriter.Column.stretchColumns(columns, 
140                                                               (int) writer.getPrintablePagesizePoints().getWidth(), 
141                                                               writer.getFontSize() / 2);
142            }
143
144            writer.setColumns(columns);                                            
145            String headerText = "";
146            // IDCOL (= Filename)
147            headerText += Bundle.getMessage("FieldID") + "\t";
148            // ADDRESSCOL:
149            headerText += Bundle.getMessage("FieldDCCAddress") + "\t";
150            // ROADNAMECOL:
151            headerText += Bundle.getMessage("FieldRoadName") + "\t";
152            // ROADNUMBERCOL:
153            headerText += Bundle.getMessage("FieldRoadNumber") + "\t";
154            // MFGCOL:
155            headerText += Bundle.getMessage("FieldManufacturer") + "\t";
156            // MODELCOL:
157            headerText += Bundle.getMessage("FieldModel") + "\t";
158            // DECODERCOL:
159            headerText += Bundle.getMessage("FieldDecoderModel") + "\t";
160            // PROTOCOL:
161            headerText += Bundle.getMessage("FieldProtocol") + "\t";
162            // OWNERCOL:
163            headerText += Bundle.getMessage("FieldOwner") + "\t";
164            // DATEUPDATECOL:
165            headerText += Bundle.getMessage("FieldDateUpdated") + "\n";
166
167            int currentPageNumber = -1;
168
169            for (RosterEntry re : l) {
170                if (currentPageNumber != writer.getPageNum()) {
171                    currentPageNumber = writer.getPageNum();
172                    try {
173                        // start a new line
174                        writer.write("\n", 0, 1);
175                        writer.setFont(null, Font.BOLD, null);
176                        writer.write(headerText);
177                        writer.setFont(null, Font.PLAIN, null);
178                        writer.leaveVerticalSpace(writer.getLineHeight()/2);
179                    } catch (IOException ex) {
180                        log.warn("error during printing", ex);
181                    }
182                }
183                if (rosterGroup != null) {
184                    if (re.getAttribute(Roster.getRosterGroupProperty(rosterGroup)) != null &&
185                            re.getAttribute(Roster.getRosterGroupProperty(rosterGroup)).equals("yes")) {
186                        re.printEntryLine(writer);
187                    }
188                } else {
189                    re.printEntryLine(writer);
190                }
191            }
192
193            // and force completion of the printing
194            // writer.close(); not needed when using try / catch
195        } catch (HardcopyWriter.PrintCanceledException ex) {
196            log.debug("Print cancelled");
197        }
198    }
199
200    // never invoked, because we overrode actionPerformed above
201    @Override
202    public jmri.util.swing.JmriPanel makePanel() {
203        throw new IllegalArgumentException("Should not be invoked");
204    }
205
206    @Override
207    public void setParameter(String parameter, String value) {
208        parameter = parameter.toLowerCase();
209        value = value.toLowerCase();
210        if (parameter.equals("ispreview")) {
211            isPreview = value.equals("true");
212        }
213    }
214
215    private final static Logger log = LoggerFactory.getLogger(PrintListAction.class);
216
217}