001package jmri.jmrit.operations.rollingstock.cars.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.rollingstock.cars.*;
017import jmri.jmrit.operations.setup.Control;
018import jmri.jmrit.operations.setup.Setup;
019import jmri.jmrit.operations.trains.TrainCommon;
020import jmri.util.davidflanagan.HardcopyWriter;
021
022/**
023 * Prints a summary of the car roster
024 * <p>
025 * This uses the older style printing, for compatibility with Java 1.1.8 in
026 * MacIntosh MRJ
027 *
028 * @author Bob Jacobsen Copyright (C) 2003
029 * @author Daniel Boudreau Copyright (C) 2023
030 */
031
032public class PrintCarRosterFrame extends OperationsFrame {
033
034    boolean _isPreview;
035    CarsTableFrame _ctf;
036
037    JComboBox<String> sortByComboBox = new JComboBox<>();
038    JComboBox<String> manifestOrientationComboBox = new JComboBox<>();
039    JComboBox<Integer> fontSizeComboBox = new JComboBox<>();
040
041    JCheckBox printCarsWithLocation = new JCheckBox(Bundle.getMessage("PrintCarsWithLocation"));
042    JCheckBox printCarLength = new JCheckBox(Bundle.getMessage("PrintCarLength"));
043    JCheckBox printCarWeight = new JCheckBox(Bundle.getMessage("PrintCarWeight"));
044    JCheckBox printCarColor = new JCheckBox(Bundle.getMessage("PrintCarColor"));
045    JCheckBox printCarOwner = new JCheckBox(Bundle.getMessage("PrintCarOwner"));
046    JCheckBox printCarBuilt = new JCheckBox(Bundle.getMessage("PrintCarBuilt"));
047    JCheckBox printCarLoad = new JCheckBox(Bundle.getMessage("PrintCarLoad"));
048    JCheckBox printCarKernel = new JCheckBox(Bundle.getMessage("PrintKernel"));
049    JCheckBox printCarValue = new JCheckBox(
050            Bundle.getMessage("PrintCar", Setup.getValueLabel()));
051    JCheckBox printCarRfid = new JCheckBox(
052            Bundle.getMessage("PrintCar", Setup.getRfidLabel()));
053    JCheckBox printCarLast = new JCheckBox(Bundle.getMessage("PrintCarLastMoved"));
054    JCheckBox printCarWait = new JCheckBox(Bundle.getMessage("PrintCarWait"));
055    JCheckBox printCarPickup = new JCheckBox(Bundle.getMessage("PrintCarPickup"));
056    JCheckBox printCarLocation = new JCheckBox(Bundle.getMessage("PrintCarLocation"));
057    JCheckBox printCarTrain = new JCheckBox(Bundle.getMessage("PrintCarTrain"));
058    JCheckBox printCarDestination = new JCheckBox(Bundle.getMessage("PrintCarDestination"));
059    JCheckBox printCarFinalDestination = new JCheckBox(Bundle.getMessage("PrintCarFinalDestination"));
060    JCheckBox printCarRoutePath = new JCheckBox(Bundle.getMessage("PrintCarRoutePath"));
061    JCheckBox printCarRWE = new JCheckBox(Bundle.getMessage("PrintCarReturnWhenEmpty"));
062    JCheckBox printCarRWL = new JCheckBox(Bundle.getMessage("PrintCarReturnWhenLoaded"));
063    JCheckBox printDivision = new JCheckBox(Bundle.getMessage("PrintCarDivision"));
064    JCheckBox printCarStatus = new JCheckBox(Bundle.getMessage("PrintCarStatus"));
065    JCheckBox printCarComment = new JCheckBox(Bundle.getMessage("PrintCarComment"));
066    JCheckBox printSpace = new JCheckBox(Bundle.getMessage("PrintSpace"));
067    JCheckBox printPage = new JCheckBox(Bundle.getMessage("PrintPage"));
068
069    JButton okayButton = new JButton(Bundle.getMessage("ButtonOK"));
070
071    public PrintCarRosterFrame(boolean isPreview, CarsTableFrame carsTableFrame) {
072        super();
073        _isPreview = isPreview;
074        _ctf = carsTableFrame;
075
076        // create panel
077        JPanel pSortBy = new JPanel();
078        pSortBy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SortBy")));
079        pSortBy.add(sortByComboBox);
080        addComboBoxAction(sortByComboBox);
081
082        JPanel pOrientation = new JPanel();
083        pOrientation.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutOrientation")));
084        pOrientation.add(manifestOrientationComboBox);
085
086        manifestOrientationComboBox.addItem(Setup.PORTRAIT);
087        manifestOrientationComboBox.addItem(Setup.LANDSCAPE);
088
089        JPanel pFontSize = new JPanel();
090        pFontSize.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutFontSize")));
091        pFontSize.add(fontSizeComboBox);
092
093        OperationsPanel.loadFontSizeComboBox(fontSizeComboBox);
094        fontSizeComboBox.setSelectedItem(Control.reportFontSize);
095
096        JPanel pPanel = new JPanel();
097        pPanel.setLayout(new GridBagLayout());
098        JScrollPane panePanel = new JScrollPane(pPanel);
099        panePanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("PrintOptions")));
100        addItemLeft(pPanel, printCarsWithLocation, 0, 0);
101        addItemLeft(pPanel, printCarLength, 0, 1);
102        addItemLeft(pPanel, printCarWeight, 0, 2);
103        addItemLeft(pPanel, printCarColor, 0, 3);
104        addItemLeft(pPanel, printCarLoad, 0, 4);
105        addItemLeft(pPanel, printCarKernel, 0, 5);
106        addItemLeft(pPanel, printCarOwner, 0, 6);
107        addItemLeft(pPanel, printCarBuilt, 0, 7);
108        addItemLeft(pPanel, printCarLast, 0, 8);
109        addItemLeft(pPanel, printCarWait, 0, 9);
110        addItemLeft(pPanel, printCarPickup, 0, 10);
111        if (Setup.isValueEnabled()) {
112            addItemLeft(pPanel, printCarValue, 0, 11);
113        }
114        if (Setup.isRfidEnabled()) {
115            addItemLeft(pPanel, printCarRfid, 0, 12);
116        }
117        addItemLeft(pPanel, printCarLocation, 0, 13);
118        addItemLeft(pPanel, printCarTrain, 0, 14);
119        addItemLeft(pPanel, printCarDestination, 0, 15);
120        addItemLeft(pPanel, printCarFinalDestination, 0, 16);
121        addItemLeft(pPanel, printCarRWE, 0, 17);
122        addItemLeft(pPanel, printCarRWL, 0, 18);
123        addItemLeft(pPanel, printDivision, 0, 19);
124        addItemLeft(pPanel, printCarStatus, 0, 20);
125        addItemLeft(pPanel, printCarRoutePath, 0, 21);
126        addItemLeft(pPanel, printCarComment, 0, 22);
127        addItemLeft(pPanel, printSpace, 0, 23);
128        addItemLeft(pPanel, printPage, 0, 24);
129
130        // set defaults
131        printCarsWithLocation.setSelected(false);
132        printCarLength.setSelected(true);
133        printCarWeight.setSelected(false);
134        printCarColor.setSelected(true);
135        printCarLoad.setSelected(false);
136        printCarKernel.setSelected(false);
137        printCarOwner.setSelected(false);
138        printCarBuilt.setSelected(false);
139        printCarLast.setSelected(false);
140        printCarWait.setSelected(false);
141        printCarPickup.setSelected(false);
142        printCarValue.setSelected(false);
143        printCarRfid.setSelected(false);
144        printCarLocation.setSelected(true);
145        printCarTrain.setSelected(false);
146        printCarDestination.setSelected(false);
147        printCarFinalDestination.setSelected(false);
148        printCarRoutePath.setSelected(false);
149        printCarRWE.setSelected(false);
150        printCarRWL.setSelected(false);
151        printDivision.setSelected(false);
152        printCarStatus.setSelected(false);
153        printCarComment.setSelected(false);
154        printSpace.setSelected(false);
155        printPage.setSelected(false);
156
157        // add tool tips
158        printSpace.setToolTipText(Bundle.getMessage("TipSelectSortByLoc"));
159        printPage.setToolTipText(Bundle.getMessage("TipSelectSortByLoc"));
160
161        JPanel pButtons = new JPanel();
162        pButtons.setLayout(new GridBagLayout());
163        pButtons.add(okayButton);
164        pButtons.setBorder(BorderFactory.createTitledBorder(""));
165        addButtonAction(okayButton);
166
167        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
168        getContentPane().add(pSortBy);
169        getContentPane().add(pOrientation);
170        getContentPane().add(pFontSize);
171        getContentPane().add(panePanel);
172        getContentPane().add(pButtons);
173
174        initMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight500));
175
176        if (_isPreview) {
177            setTitle(Bundle.getMessage("MenuItemPreview"));
178        } else {
179            setTitle(Bundle.getMessage("MenuItemPrint"));
180        }
181        loadSortByComboBox(sortByComboBox);
182        updateLocationCheckboxes();
183    }
184
185    private void loadSortByComboBox(JComboBox<String> box) {
186        box.removeAllItems();
187        for (int i = _ctf.carsTableModel.SORTBY_NUMBER; i <= _ctf.carsTableModel.SORTBY_COMMENT; i++) {
188            box.addItem(_ctf.carsTableModel.getSortByName(i));
189        }
190        box.setSelectedItem(_ctf.carsTableModel.getSortByName());
191    }
192
193    @Override
194    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
195        setVisible(false);
196        printCars();
197    }
198
199    @Override
200    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
201        updateLocationCheckboxes();
202    }
203
204    private void updateLocationCheckboxes() {
205        if (sortByComboBox.getSelectedItem() != null &&
206                sortByComboBox.getSelectedItem()
207                        .equals(_ctf.carsTableModel.getSortByName(_ctf.carsTableModel.SORTBY_LOCATION))) {
208            printSpace.setEnabled(true);
209            printPage.setEnabled(true);
210        } else {
211            printSpace.setEnabled(false);
212            printPage.setEnabled(false);
213            printSpace.setSelected(false);
214            printPage.setSelected(false);
215        }
216    }
217
218    int numberCharPerLine;
219
220    private void printCars() {
221        boolean landscape = false;
222        if (manifestOrientationComboBox.getSelectedItem() != null &&
223                manifestOrientationComboBox.getSelectedItem().equals(Setup.LANDSCAPE)) {
224            landscape = true;
225        }
226
227        int fontSize = (int) fontSizeComboBox.getSelectedItem();
228
229        // obtain a HardcopyWriter to do this
230        try (HardcopyWriter writer = new HardcopyWriter(new Frame(), Bundle.getMessage("TitleCarRoster"), fontSize, .5,
231                .5, .5, .5, _isPreview, "", landscape, true, null)) {
232
233            numberCharPerLine = writer.getCharactersPerLine();
234
235            printHeader(writer);
236
237            printRoster(writer);
238
239        } catch (HardcopyWriter.PrintCanceledException ex) {
240            log.debug("Print cancelled");
241        } catch (IOException we) {
242            log.error("Error printing car roster: {}", we.getLocalizedMessage());
243        }
244    }
245
246    private void printHeader(HardcopyWriter writer) throws IOException {
247        String s = padAttribute(Bundle.getMessage("Number"), Control.max_len_string_print_road_number) +
248                padAttribute(Bundle.getMessage("Road"),
249                        InstanceManager.getDefault(CarRoads.class).getMaxNameLength()) +
250                padAttribute(Bundle.getMessage("Type"),
251                        InstanceManager.getDefault(CarTypes.class).getMaxFullNameLength()) +
252                (printCarLength.isSelected() ? Bundle.getMessage("Len") + "  " : "") +
253                (printCarWeight.isSelected() ? "     " : "") +
254                (printCarColor.isSelected()
255                        ? padAttribute(Bundle.getMessage("Color"),
256                                InstanceManager.getDefault(CarColors.class).getMaxNameLength())
257                        : "") +
258                (printCarLoad.isSelected()
259                        ? padAttribute(Bundle.getMessage("Load"),
260                                InstanceManager.getDefault(CarLoads.class).getMaxNameLength())
261                        : "") +
262                (printCarKernel.isSelected() ? padAttribute(("Kernel"), Control.max_len_string_attibute) : "") +
263                (printCarOwner.isSelected()
264                        ? padAttribute(Bundle.getMessage("Owner"),
265                                InstanceManager.getDefault(CarOwners.class).getMaxNameLength())
266                        : "") +
267                (printCarBuilt.isSelected() ? Bundle.getMessage("Built") + " " : "") +
268                (printCarLast.isSelected() ? Bundle.getMessage("LastMoved") + " " : "") +
269                (printCarWait.isSelected() ? Bundle.getMessage("Wait") + " " : "") +
270                (printCarPickup.isSelected() ? padAttribute(Bundle.getMessage("Pickup"), 10) : "") +
271                (printCarValue.isSelected() ? padAttribute(Setup.getValueLabel(), Control.max_len_string_attibute)
272                        : "") +
273                (printCarRfid.isSelected() ? padAttribute(Setup.getRfidLabel(), Control.max_len_string_attibute)
274                        : "") +
275                (printCarLocation.isSelected()
276                        ? padAttribute(Bundle.getMessage("Location"),
277                                InstanceManager.getDefault(LocationManager.class)
278                                        .getMaxLocationAndTrackNameLength() +
279                                        3)
280                        : "") +
281                (printCarTrain.isSelected()
282                        ? padAttribute(Bundle.getMessage("Train"), Control.max_len_string_train_name / 2)
283                        : "") +
284                (printCarDestination.isSelected()
285                        ? padAttribute(Bundle.getMessage("Destination"),
286                                InstanceManager.getDefault(LocationManager.class)
287                                        .getMaxLocationAndTrackNameLength() +
288                                        3)
289                        : "") +
290                (printCarFinalDestination.isSelected()
291                        ? padAttribute(Bundle.getMessage("FinalDestination"),
292                                InstanceManager.getDefault(LocationManager.class)
293                                        .getMaxLocationAndTrackNameLength() +
294                                        3)
295                        : "") +
296                (printCarRWE.isSelected()
297                        ? padAttribute(Bundle.getMessage("ReturnWhenEmpty"),
298                                InstanceManager.getDefault(LocationManager.class)
299                                        .getMaxLocationAndTrackNameLength() +
300                                        3)
301                        : "") +
302                (printCarRWL.isSelected()
303                        ? padAttribute(Bundle.getMessage("ReturnWhenLoaded"),
304                                InstanceManager.getDefault(LocationManager.class)
305                                        .getMaxLocationAndTrackNameLength() +
306                                        3)
307                        : "") +
308                (printDivision.isSelected() ? Bundle.getMessage("HomeDivision") + " " : "") +
309                (printCarStatus.isSelected() ? Bundle.getMessage("Status") + " " : "") +
310                (printCarRoutePath.isSelected()
311                        ? padAttribute(Bundle.getMessage("Route"),
312                                InstanceManager.getDefault(LocationManager.class)
313                                        .getMaxLocationAndTrackNameLength() +
314                                        3)
315                        : "") +
316                (printCarComment.isSelected() ? Bundle.getMessage("Comment") : "");
317        if (s.length() > numberCharPerLine) {
318            s = s.substring(0, numberCharPerLine);
319            writer.write(Bundle.getMessage("WarningTextPage") + NEW_LINE);
320        }
321        writer.write(s + NEW_LINE);
322    }
323
324    private void printRoster(HardcopyWriter writer) throws IOException {
325        // Loop through the Roster, printing as needed
326        String location = "";
327        String number;
328        String road;
329        String type;
330        String length = "";
331        String weight = "";
332        String color = "";
333        String owner = "";
334        String built = "";
335        String load = "";
336        String kernel = "";
337        String train = "";
338        String destination = "";
339        String finalDestination = "";
340        String returnWhenEmpty = "";
341        String returnWhenLoaded = "";
342        String division = "";
343        String value = "";
344        String rfid = "";
345        String last = "";
346        String wait = "";
347        String schedule = "";
348        String status = "";
349        String routePath = "";
350        String comment = "";
351        String previousLocation = null;
352        List<Car> cars = _ctf.carsTableModel.getCarList(sortByComboBox.getSelectedIndex());
353        for (Car car : cars) {
354            if (printCarsWithLocation.isSelected() && car.getLocation() == null) {
355                continue; // car doesn't have a location skip
356            }
357            location = "";
358            destination = "";
359            finalDestination = "";
360            returnWhenEmpty = "";
361            returnWhenLoaded = "";
362
363            if (printCarLocation.isSelected()) {
364                if (car.getLocation() != null) {
365                    location = car.getLocationName().trim() + " - " + car.getTrackName().trim();
366                }
367                location = padAttribute(location,
368                        InstanceManager.getDefault(LocationManager.class).getMaxLocationAndTrackNameLength() +
369                                3);
370            }
371            // Page break between locations?
372            if (previousLocation != null &&
373                    !car.getLocationName().trim().equals(previousLocation) &&
374                    printPage.isSelected()) {
375                writer.pageBreak();
376                printHeader(writer);
377            } // Add a line between locations?
378            else if (previousLocation != null &&
379                    !car.getLocationName().trim().equals(previousLocation) &&
380                    printSpace.isSelected()) {
381                writer.write(NEW_LINE);
382            }
383            previousLocation = car.getLocationName().trim();
384
385            // car number
386            number = padAttribute(car.getNumber().trim(), Control.max_len_string_print_road_number);
387            // car road
388            road = padAttribute(car.getRoadName().trim(),
389                    InstanceManager.getDefault(CarRoads.class).getMaxNameLength());
390            // car type
391            type = padAttribute(car.getTypeName().trim(),
392                    InstanceManager.getDefault(CarTypes.class).getMaxFullNameLength());
393
394            if (printCarLength.isSelected()) {
395                length = padAttribute(car.getLength().trim(), Control.max_len_string_length_name);
396            }
397            if (printCarWeight.isSelected()) {
398                weight = padAttribute(car.getWeight().trim(), Control.max_len_string_weight_name);
399            }
400            if (printCarColor.isSelected()) {
401                color = padAttribute(car.getColor().trim(),
402                        InstanceManager.getDefault(CarColors.class).getMaxNameLength());
403            }
404            if (printCarLoad.isSelected()) {
405                load = padAttribute(car.getLoadName().trim(),
406                        InstanceManager.getDefault(CarLoads.class).getMaxNameLength());
407            }
408            if (printCarKernel.isSelected()) {
409                kernel = padAttribute(car.getKernelName().trim(), Control.max_len_string_attibute);
410            }
411            if (printCarOwner.isSelected()) {
412                owner = padAttribute(car.getOwnerName().trim(),
413                        InstanceManager.getDefault(CarOwners.class).getMaxNameLength());
414            }
415            if (printCarBuilt.isSelected()) {
416                built = padAttribute(car.getBuilt().trim(), Control.max_len_string_built_name);
417            }
418            if (printCarLast.isSelected()) {
419                last = padAttribute(car.getSortDate().split(" ")[0], 10);
420            }
421            if (printCarWait.isSelected()) {
422                wait = padAttribute(Integer.toString(car.getWait()), 4);
423            }
424            if (printCarPickup.isSelected()) {
425                schedule = padAttribute(car.getPickupScheduleName(), 10);
426            }
427            if (printCarValue.isSelected()) {
428                value = padAttribute(car.getValue().trim(), Control.max_len_string_attibute);
429            }
430            if (printCarRfid.isSelected()) {
431                rfid = padAttribute(car.getRfid().trim(), Control.max_len_string_attibute);
432            }
433            // pad out train to half of its maximum length
434            if (printCarTrain.isSelected()) {
435                train = padAttribute(car.getTrainName().trim(), Control.max_len_string_train_name / 2);
436            }
437            if (printCarDestination.isSelected()) {
438                if (car.getDestination() != null) {
439                    destination =
440                            car.getDestinationName().trim() + " - " + car.getDestinationTrackName().trim();
441                }
442                destination = padAttribute(destination,
443                        InstanceManager.getDefault(LocationManager.class).getMaxLocationAndTrackNameLength() +
444                                3);
445            }
446            if (printCarFinalDestination.isSelected()) {
447                if (car.getFinalDestination() != null) {
448                    finalDestination = car.getFinalDestinationName().trim();
449                    if (car.getFinalDestinationTrack() != null) {
450                        finalDestination = finalDestination +
451                                " - " +
452                                car.getFinalDestinationTrackName().trim();
453                    }
454                }
455                finalDestination = padAttribute(finalDestination,
456                        InstanceManager.getDefault(LocationManager.class).getMaxLocationAndTrackNameLength() +
457                                3);
458            }
459            // long route paths will wrap
460            if (printCarRoutePath.isSelected()) {
461                routePath = car.getRoutePath() + " ";
462            }
463            if (printCarRWE.isSelected()) {
464                if (car.getReturnWhenEmptyDestination() != null) {
465                    returnWhenEmpty = car.getReturnWhenEmptyDestinationName().trim() +
466                            " - " +
467                            car.getReturnWhenEmptyDestTrackName().trim();
468                }
469                returnWhenEmpty = padAttribute(returnWhenEmpty,
470                        InstanceManager.getDefault(LocationManager.class).getMaxLocationAndTrackNameLength() +
471                                3);
472            }
473            if (printCarRWL.isSelected()) {
474                if (car.getReturnWhenLoadedDestination() != null) {
475                    returnWhenLoaded = car.getReturnWhenLoadedDestinationName().trim() +
476                            " - " +
477                            car.getReturnWhenLoadedDestTrackName().trim();
478                }
479                returnWhenLoaded = padAttribute(returnWhenLoaded,
480                        InstanceManager.getDefault(LocationManager.class).getMaxLocationAndTrackNameLength() +
481                                3);
482            }
483            if (printDivision.isSelected()) {
484                division = padAttribute(car.getDivisionName(), Bundle.getMessage("HomeDivision").length());
485            }
486            if (printCarStatus.isSelected()) {
487                status = padAttribute(car.getStatus(), Bundle.getMessage("Status").length());
488            }
489            // comment gets trimmed by line length
490            if (printCarComment.isSelected()) {
491                comment = car.getComment().trim();
492            }
493
494            String s = number +
495                    road +
496                    type +
497                    length +
498                    weight +
499                    color +
500                    load +
501                    kernel +
502                    owner +
503                    built +
504                    last +
505                    wait +
506                    schedule +
507                    value +
508                    rfid +
509                    location +
510                    train +
511                    destination +
512                    finalDestination +
513                    returnWhenEmpty +
514                    returnWhenLoaded +
515                    division +
516                    status +
517                    routePath +
518                    comment;
519
520            s = s.trim();
521            if (s.length() > numberCharPerLine) {
522                writer.write(s.substring(0, numberCharPerLine) + NEW_LINE);
523                s = s.substring(numberCharPerLine, s.length());
524                String tab = getTab();
525                int subStringLength = numberCharPerLine - tab.length();
526                while (tab.length() + s.length() > numberCharPerLine) {
527                    writer.write(tab + s.substring(0, subStringLength) + NEW_LINE);
528                    s = s.substring(subStringLength, s.length());
529                }
530                s = tab + s;
531            }
532            writer.write(s + NEW_LINE);
533        }
534    }
535
536    private String padAttribute(String attribute, int length) {
537        return TrainCommon.padAndTruncate(attribute, length) + TrainCommon.SPACE;
538    }
539
540    private String getTab() {
541        return padAttribute("",
542                Control.max_len_string_print_road_number +
543                        InstanceManager.getDefault(CarRoads.class).getMaxNameLength() +
544                        1);
545    }
546
547    private final static Logger log = LoggerFactory.getLogger(PrintCarRosterFrame.class);
548}