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