001package jmri.jmrit.operations.locations.schedules.tools;
002
003import java.awt.*;
004
005import javax.swing.*;
006
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010import jmri.InstanceManager;
011import jmri.jmrit.operations.OperationsFrame;
012import jmri.jmrit.operations.locations.*;
013import jmri.jmrit.operations.locations.schedules.Schedule;
014import jmri.jmrit.operations.locations.schedules.ScheduleItem;
015import jmri.jmrit.operations.rollingstock.cars.CarLoads;
016import jmri.jmrit.operations.rollingstock.cars.CarTypes;
017import jmri.jmrit.operations.rollingstock.cars.tools.CarLoadEditFrameAction;
018import jmri.jmrit.operations.rollingstock.cars.tools.PrintCarLoadsAction;
019import jmri.jmrit.operations.setup.Control;
020
021/**
022 * Frame to display spurs with schedules and their loads
023 *
024 * @author Dan Boudreau Copyright (C) 2012, 2015, 2021, 2025
025 */
026public class SchedulesByLoadFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
027
028    // managers'
029    LocationManager locationManager = InstanceManager.getDefault(LocationManager.class);
030    CarLoads carLoads = InstanceManager.getDefault(CarLoads.class);
031    CarTypes carTypes = InstanceManager.getDefault(CarTypes.class);
032
033    // combo box
034    JComboBox<String> typesComboBox = carTypes.getComboBox();
035    JComboBox<String> loadsComboBox = new JComboBox<>();
036
037    // panels
038    JPanel locationsPanel;
039
040    // checkbox
041    JCheckBox allLoadsCheckBox = new JCheckBox(Bundle.getMessage("allLoads"));
042    JCheckBox allTypesCheckBox = new JCheckBox(Bundle.getMessage("allTypes"));
043
044    public SchedulesByLoadFrame() {
045        super(Bundle.getMessage("MenuItemShowSchedulesByLoad"));
046
047        // the following code sets the frame's initial state
048        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
049
050        // load the panel
051        JPanel p1 = new JPanel();
052        p1.setMaximumSize(new Dimension(2000, 200));
053        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
054
055        JPanel type = new JPanel();
056        type.setLayout(new GridBagLayout());
057        type.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
058        addItem(type, typesComboBox, 0, 0);
059        addItem(type, allTypesCheckBox, 1, 0);
060
061        JPanel load = new JPanel();
062        load.setLayout(new GridBagLayout());
063        load.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Load")));
064        addItem(load, loadsComboBox, 0, 0);
065        addItem(load, allLoadsCheckBox, 1, 0);
066
067        p1.add(type);
068        p1.add(load);
069
070        locationsPanel = new JPanel();
071        locationsPanel.setLayout(new GridBagLayout());
072        JScrollPane locationsPane = new JScrollPane(locationsPanel);
073        locationsPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
074        locationsPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location")));
075
076        getContentPane().add(p1);
077        getContentPane().add(locationsPane);
078
079        addComboBoxAction(typesComboBox);
080        addComboBoxAction(loadsComboBox);
081
082        addCheckBoxAction(allTypesCheckBox);
083        addCheckBoxAction(allLoadsCheckBox);
084
085        // property changes
086        locationManager.addPropertyChangeListener(this);
087        carTypes.addPropertyChangeListener(this);
088        carLoads.addPropertyChangeListener(this);
089
090        // build menu
091        JMenuBar menuBar = new JMenuBar();
092        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
093        toolMenu.add(new CarLoadEditFrameAction());
094        toolMenu.addSeparator();
095        toolMenu.add(new PrintCarLoadsAction(true));
096        toolMenu.add(new PrintCarLoadsAction(false));
097        menuBar.add(toolMenu);
098        setJMenuBar(menuBar);
099        addHelpMenu("package.jmri.jmrit.operations.Operations_ShowSchedulesByCarTypeAndLoad", true); // NOI18N
100
101        // select first item to load contents
102        typesComboBox.setSelectedIndex(0);
103
104        initMinimumSize(new Dimension(Control.panelWidth1025, Control.panelHeight250));
105    }
106
107    @Override
108    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
109        if (ae.getSource() == typesComboBox) {
110            updateLoadComboBox();
111        }
112        if (ae.getSource() == loadsComboBox) {
113            updateLocations();
114        }
115
116    }
117
118    @Override
119    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
120        typesComboBox.setEnabled(!allTypesCheckBox.isSelected());
121        loadsComboBox.setEnabled(!allLoadsCheckBox.isSelected());
122        updateLoadComboBox();
123        updateLocations();
124    }
125
126    private void updateLoadComboBox() {
127        if (allTypesCheckBox.isSelected()) {
128            carLoads.updateComboBox(loadsComboBox);
129        } else if (typesComboBox.getSelectedItem() != null) {
130            String type = (String) typesComboBox.getSelectedItem();
131            carLoads.updateComboBox(type, loadsComboBox);
132        }
133    }
134
135    private void updateLocations() {
136        locationsPanel.removeAll();
137        String type = (String) typesComboBox.getSelectedItem();
138        String load = (String) loadsComboBox.getSelectedItem();
139        // determine if load is default empty or load
140        boolean defaultLoad = carLoads.getDefaultLoadName().equals(load) || carLoads.getDefaultEmptyName().equals(load);
141        log.debug("Update locations for type ({}) load ({})", type, load);
142
143        // build header
144        int row = 0;
145        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Spur")), 1, row);
146        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Schedule")), 2, row);
147        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Type")), 3, row);
148        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Random")), 4, row);
149        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Delivery")), 5, row);
150        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Road")), 6, row);
151        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Receive")), 7, row);
152        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Ship")), 8, row);
153        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("destinationTrack")), 9, row);
154        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Pickup")), 10, row++);
155
156        // now load data
157        for (Location location : locationManager.getLocationsByNameList()) {
158            // only spurs have schedules
159            if (!location.hasSpurs())
160                continue;
161            addItemLeft(locationsPanel, new JLabel(location.getName()), 0, row++);
162            // now look for a spur with a schedule
163            for (Track spur : location.getTracksByNameList(Track.SPUR)) {
164                Schedule sch = spur.getSchedule();
165                if (sch == null) {
166                    continue;
167                }
168                // listen for changes
169                spur.removePropertyChangeListener(this);
170                spur.addPropertyChangeListener(this);
171                sch.removePropertyChangeListener(this);
172                sch.addPropertyChangeListener(this);
173
174                // determine if schedule is requesting car type and load
175                for (ScheduleItem si : sch.getItemsBySequenceList()) {
176                    // skip if car type doesn't carry load name
177                    if (allTypesCheckBox.isSelected() &&
178                            !allLoadsCheckBox.isSelected() &&
179                            !carLoads.containsName(si.getTypeName(), load)) {
180                        continue;
181                    }
182                    if ((allTypesCheckBox.isSelected() || si.getTypeName().equals(type)) &&
183                            (allLoadsCheckBox.isSelected() ||
184                                    si.getReceiveLoadName().equals(load) ||
185                                    si.getReceiveLoadName().equals(ScheduleItem.NONE) ||
186                                    si.getShipLoadName().equals(load) ||
187                                    (si.getShipLoadName().equals(ScheduleItem.NONE) && defaultLoad))) {
188                        // is the schedule item valid?
189                        String status = spur.checkScheduleValid();
190                        if (!status.equals(Schedule.SCHEDULE_OKAY)) {
191                            JLabel errorSchedule = new JLabel("  " + status);
192                            errorSchedule.setForeground(Color.RED);
193                            addItemLeft(locationsPanel, errorSchedule, 0, row);
194                        }
195                        addItemLeft(locationsPanel, new JLabel(spur.getName()), 1, row);
196                        addItemLeft(locationsPanel, new JLabel(spur.getScheduleName()), 2, row);
197                        addItemLeft(locationsPanel, new JLabel(si.getTypeName()), 3, row);
198                        addItemLeft(locationsPanel, new JLabel(si.getRandom()), 4, row);
199                        addItemLeft(locationsPanel, new JLabel(si.getSetoutTrainScheduleName()), 5, row);
200                        addItemLeft(locationsPanel, new JLabel(si.getRoadName()), 6, row);
201                        // report if spur can't service the selected load
202                        if (!allLoadsCheckBox.isSelected() &&
203                                si.getReceiveLoadName().equals(ScheduleItem.NONE) &&
204                                !spur.isLoadNameAndCarTypeAccepted(load, type)) {
205                            JLabel warnLoad =
206                                    new JLabel(Bundle.getMessage("spurNotTypeLoad", spur.getName(), type, load));
207                            warnLoad.setForeground(Color.BLUE);
208                            addItemLeft(locationsPanel, warnLoad, 7, row);
209                        } else {
210                            addItemLeft(locationsPanel, new JLabel(si.getReceiveLoadName()), 7, row);
211                        }
212                        addItemLeft(locationsPanel, new JLabel(si.getShipLoadName()), 8, row);
213                        // now the destination and track
214                        if (si.getDestination() != null) {
215                            addItemLeft(locationsPanel,
216                                    new JLabel(si.getDestinationName() + " (" + si.getDestinationTrackName() + ")"), 9,
217                                    row);
218                        }
219                        addItemLeft(locationsPanel, new JLabel(si.getPickupTrainScheduleName()), 10, row++);
220                    }
221                }
222            }
223        }
224        locationsPanel.revalidate();
225        revalidate();
226        repaint();
227    }
228
229    @Override
230    public void dispose() {
231        locationManager.removePropertyChangeListener(this);
232        carTypes.removePropertyChangeListener(this);
233        carLoads.removePropertyChangeListener(this);
234        for (Track spur : locationManager.getTracks(Track.SPUR)) {
235            Schedule sch = spur.getSchedule();
236            if (sch == null) {
237                continue;
238            }
239            spur.removePropertyChangeListener(this);
240            sch.removePropertyChangeListener(this);
241        }
242        super.dispose();
243    }
244
245    @Override
246    public void propertyChange(java.beans.PropertyChangeEvent e) {
247        log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N
248
249        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY)) {
250            carTypes.updateComboBox(typesComboBox);
251        }
252        if (e.getSource().getClass().equals(CarLoads.class)) {
253            carLoads.updateComboBox((String) typesComboBox.getSelectedItem(), loadsComboBox);
254        }
255        if (e.getSource().getClass().equals(Schedule.class) ||
256                e.getSource().getClass().equals(LocationManager.class) ||
257                e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) ||
258                e.getPropertyName().equals(Track.ROADS_CHANGED_PROPERTY) ||
259                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) {
260            updateLocations();
261        }
262    }
263
264    private static final Logger log = LoggerFactory.getLogger(SchedulesByLoadFrame.class);
265
266}