001package jmri.jmrit.operations.trains.tools;
002
003import java.awt.*;
004import java.io.IOException;
005import java.util.List;
006
007import javax.swing.*;
008
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012import jmri.InstanceManager;
013import jmri.jmrit.operations.OperationsFrame;
014import jmri.jmrit.operations.OperationsPanel;
015import jmri.jmrit.operations.locations.LocationManager;
016import jmri.jmrit.operations.routes.Route;
017import jmri.jmrit.operations.routes.RouteLocation;
018import jmri.jmrit.operations.setup.Control;
019import jmri.jmrit.operations.setup.Setup;
020import jmri.jmrit.operations.trains.*;
021import jmri.jmrit.operations.trains.gui.TrainsTableFrame;
022import jmri.jmrit.operations.trains.gui.TrainsTableModel;
023import jmri.util.davidflanagan.HardcopyWriter;
024
025/**
026 * Prints a summary of a train or trains. The trains list is controlled by the
027 * "Show All" checkbox and the "Build" checkboxes in the TrainsTableFrame.
028 * <p>
029 * This uses the older style printing, for compatibility with Java 1.1.8 in
030 * Macintosh MRJ
031 *
032 * @author Bob Jacobsen Copyright (C) 2003
033 * @author Dennis Miller Copyright (C) 2005
034 * @author Daniel Boudreau Copyright (C) 2009, 2023
035 */
036public class PrintTrainsFrame extends OperationsFrame {
037
038    static final String TAB = "\t"; // NOI18N
039    static final char FORM_FEED = '\f'; // NOI18N
040
041    public static final int MAX_NAME_LENGTH = Control.max_len_string_train_name - 10;
042
043    boolean _isPreview;
044    Train _train;
045    TrainsTableFrame _trainsTableFrame;
046
047    JComboBox<String> sortByComboBox = new JComboBox<>();
048    JComboBox<String> manifestOrientationComboBox = new JComboBox<>();
049    JComboBox<Integer> fontSizeComboBox = new JComboBox<>();
050
051    JCheckBox printSummary = new JCheckBox(Bundle.getMessage("PrintSummary"));
052    JCheckBox printDetails = new JCheckBox(Bundle.getMessage("PrintDetails"));
053    JButton okayButton = new JButton(Bundle.getMessage("ButtonOK"));
054
055    public PrintTrainsFrame(boolean isPreview, Train train) {
056        _isPreview = isPreview;
057        _train = train;
058        printTrain();
059    }
060
061    public PrintTrainsFrame(boolean isPreview, TrainsTableFrame trainsTableFrame) {
062        _isPreview = isPreview;
063        _trainsTableFrame = trainsTableFrame;
064
065        // create panel
066        JPanel pSortBy = new JPanel();
067        pSortBy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SortBy")));
068        pSortBy.add(sortByComboBox);
069
070        loadSortByComboBox(sortByComboBox);
071        addComboBoxAction(sortByComboBox);
072
073        JPanel pOrientation = new JPanel();
074        pOrientation.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutOrientation")));
075        pOrientation.add(manifestOrientationComboBox);
076
077        manifestOrientationComboBox.addItem(Setup.PORTRAIT);
078        manifestOrientationComboBox.addItem(Setup.LANDSCAPE);
079        //        manifestOrientationComboBox.setSelectedItem(Setup.LANDSCAPE);
080
081        JPanel pFontSize = new JPanel();
082        pFontSize.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutFontSize")));
083        pFontSize.add(fontSizeComboBox);
084
085        OperationsPanel.loadFontSizeComboBox(fontSizeComboBox);
086        fontSizeComboBox.setSelectedItem(Control.reportFontSize);
087
088        // create panel
089        JPanel pPanel = new JPanel();
090        pPanel.setLayout(new GridBagLayout());
091        pPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PrintOptions")));
092        addItemLeft(pPanel, printSummary, 0, 0);
093        addItemLeft(pPanel, printDetails, 0, 1);
094
095        printSummary.setSelected(true);
096        printDetails.setSelected(true);
097
098        // add tool tips
099        JPanel pButtons = new JPanel();
100        pButtons.setLayout(new GridBagLayout());
101        pButtons.add(okayButton);
102        addButtonAction(okayButton);
103
104        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
105        getContentPane().add(pSortBy);
106        getContentPane().add(pOrientation);
107        getContentPane().add(pFontSize);
108        getContentPane().add(pPanel);
109        getContentPane().add(pButtons);
110        setPreferredSize(null);
111        if (_isPreview) {
112            setTitle(Bundle.getMessage("MenuItemPreview"));
113        } else {
114            setTitle(Bundle.getMessage("MenuItemPrint"));
115        }
116        initMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight250));
117    }
118
119    @Override
120    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
121        setVisible(false);
122        printTrains();
123    }
124
125    private void printTrains() {
126        if (!printSummary.isSelected() && !printDetails.isSelected()) {
127            return;
128        }
129
130        // obtain a HardcopyWriter to do this
131        boolean isLandscape = false;
132        if (manifestOrientationComboBox.getSelectedItem() != null &&
133                manifestOrientationComboBox.getSelectedItem().equals(Setup.LANDSCAPE)) {
134            isLandscape = true;
135        }
136
137        int fontSize = (int) fontSizeComboBox.getSelectedItem();
138        try (HardcopyWriter writer = new HardcopyWriter(new Frame(), Bundle.getMessage("TitleTrainsTable"),
139                fontSize, .5, .5, .5, .5, _isPreview, "", isLandscape, true, null, null);) {
140
141            List<Train> trains = _trainsTableFrame.getSortByList((String) sortByComboBox.getSelectedItem());
142
143            if (printSummary.isSelected()) {
144                printSummaryTrains(writer, trains, _trainsTableFrame);
145                if (printDetails.isSelected()) {
146                    writer.write(FORM_FEED); // new page
147                }
148            }
149
150            if (printDetails.isSelected()) {
151                // now do the details for each train
152                for (Train train : trains) {
153                    if ((train.isBuildEnabled() || _trainsTableFrame.showAllBox.isSelected()) &&
154                            train.getRoute() != null) {
155                        List<RouteLocation> route = train.getRoute().getLocationsBySequenceList();
156                        // determine if another detailed summary can fit on the same page
157                        if (writer.getLinesPerPage() - writer.getCurrentLineNumber() < route.size() +
158                                getNumberOfLines(writer, Bundle.getMessage("Comment") + ": " + train.getComment()) +
159                                NUMBER_OF_HEADER_LINES) {
160                            writer.write(FORM_FEED);
161                        } else if (writer.getCurrentLineNumber() > 0) {
162                            writer.write(NEW_LINE);
163                        }
164                        printTrain(writer, train);
165                    }
166                }
167            }
168        } catch (HardcopyWriter.PrintCanceledException ex) {
169            log.debug("Print cancelled");
170        } catch (IOException e1) {
171            log.error("Exception in print train details: {}", e1.getLocalizedMessage());
172        }
173    }
174
175    private void printSummaryTrains(HardcopyWriter writer, List<Train> trains, TrainsTableFrame trainsTableFrame)
176            throws IOException {
177        int maxLineLength = writer.getCharactersPerLine() - 1;
178        int maxTrainNameLength = InstanceManager.getDefault(TrainManager.class).getMaxTrainNameLength();
179        int maxLocationNameLength = InstanceManager.getDefault(LocationManager.class).getMaxLocationNameLength();
180        String s = Bundle.getMessage("Time") +
181                "  " +
182                truncate(Bundle.getMessage("Name"), maxTrainNameLength) +
183                truncate(Bundle.getMessage("Description")) +
184                truncate(Bundle.getMessage("Route")) +
185                truncate(Bundle.getMessage("Departs"), maxLocationNameLength) +
186                truncate(Bundle.getMessage("Terminates"), maxLocationNameLength);
187        writer.write(truncate(s, maxLineLength) + NEW_LINE);
188        for (Train train : trains) {
189            if (train.isBuildEnabled() || trainsTableFrame.showAllBox.isSelected()) {
190                String name = truncate(train.getName(), maxTrainNameLength);
191                String desc = truncate(train.getDescription());
192                String route = truncate(train.getTrainRouteName());
193                String departs = truncate(train.getTrainDepartsName(), maxLocationNameLength);
194                String terminates = truncate(train.getTrainTerminatesName(), maxLocationNameLength);
195
196                s = train.getDepartureTime() +
197                        " " +
198                        name +
199                        desc +
200                        route +
201                        departs +
202                        terminates;
203                writer.write(truncate(s, maxLineLength) + NEW_LINE);
204            }
205        }
206    }
207
208    private String truncate(String string) {
209        return truncate(string, MAX_NAME_LENGTH);
210    }
211
212    private String truncate(String string, int length) {
213        return TrainCommon.padAndTruncate(string, length) + TrainCommon.SPACE;
214    }
215
216    private void printTrain() {
217        if (_train == null) {
218            return;
219        }
220        // obtain a HardcopyWriter to do this
221        try (HardcopyWriter writer = new HardcopyWriter(new Frame(), Bundle.getMessage("TitleTrain", _train.getName()),
222                Control.reportFontSize, .5, .5, .5, .5, _isPreview)) {
223
224            printTrain(writer, _train);
225        } catch (HardcopyWriter.PrintCanceledException ex) {
226            log.debug("Print cancelled");
227        } catch (IOException ex) {
228            log.error("Exception in print train: {}", ex.getLocalizedMessage());
229        }
230    }
231
232    // 7 lines of header plus NEW_LINE at start
233    private static final int NUMBER_OF_HEADER_LINES = 8;
234
235    private void printTrain(HardcopyWriter writer, Train train) throws IOException {
236        String s = Bundle.getMessage("Name") + ": " + train.getName() + NEW_LINE;
237        writer.write(s);
238        s = Bundle.getMessage("Description") + ": " + train.getDescription() + NEW_LINE;
239        writer.write(s);
240        s = Bundle.getMessage("Departs") + ": " + train.getTrainDepartsName() + NEW_LINE;
241        writer.write(s);
242        s = Bundle.getMessage("DepartTime") + ": " + train.getDepartureTime() + NEW_LINE;
243        writer.write(s);
244        s = Bundle.getMessage("Terminates") + ": " + train.getTrainTerminatesName() + NEW_LINE;
245        writer.write(s);
246
247        writer.write(NEW_LINE);
248
249        s = Bundle.getMessage("Route") + ": " + train.getTrainRouteName() + NEW_LINE;
250        writer.write(s);
251        Route route = train.getRoute();
252        if (route != null) {
253            for (RouteLocation rl : route.getLocationsBySequenceList()) {
254                s = TAB + rl.getName() + NEW_LINE;
255                writer.write(s);
256            }
257        }
258        if (!train.getComment().equals(Train.NONE)) {
259            s = Bundle.getMessage("Comment") + ": " + train.getComment() + NEW_LINE;
260            writer.write(s);
261        }
262    }
263
264    private void loadSortByComboBox(JComboBox<String> box) {
265        box.addItem(TrainsTableModel.IDCOLUMNNAME);
266        box.addItem(TrainsTableModel.TIMECOLUMNNAME);
267        box.addItem(TrainsTableModel.NAMECOLUMNNAME);
268        box.addItem(TrainsTableModel.DESCRIPTIONCOLUMNNAME);
269        box.addItem(TrainsTableModel.ROUTECOLUMNNAME);
270        box.addItem(TrainsTableModel.DEPARTSCOLUMNNAME);
271        box.addItem(TrainsTableModel.TERMINATESCOLUMNNAME);
272        box.addItem(TrainsTableModel.STATUSCOLUMNNAME);
273
274        box.setSelectedItem(_trainsTableFrame.getSortBy());
275    }
276
277    private int getNumberOfLines(HardcopyWriter writer, String string) {
278        String[] lines = string.split(NEW_LINE);
279        int count = lines.length;
280        // any long lines that exceed the page width?
281        for (String line : lines) {
282            int add = line.length() / writer.getCharactersPerLine();
283            count = count + add;
284        }
285        return count;
286    }
287
288    private final static Logger log = LoggerFactory.getLogger(PrintTrainsFrame.class);
289}