001package jmri.jmrit.operations.locations.schedules.tools;
002
003import java.awt.Dimension;
004import java.awt.GridBagLayout;
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
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("Locations")));
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.add(new PrintCarLoadsAction(true));
095        toolMenu.add(new PrintCarLoadsAction(false));
096        menuBar.add(toolMenu);
097        setJMenuBar(menuBar);
098        addHelpMenu("package.jmri.jmrit.operations.Operations_ShowSchedulesByCarTypeAndLoad", true); // NOI18N
099
100        // select first item to load contents
101        typesComboBox.setSelectedIndex(0);
102
103        initMinimumSize(new Dimension(Control.panelWidth700, Control.panelHeight250));
104    }
105
106    @Override
107    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
108        if (ae.getSource() == typesComboBox) {
109            updateLoadComboBox();
110        }
111        if (ae.getSource() == loadsComboBox) {
112            updateLocations();
113        }
114
115    }
116
117    @Override
118    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
119        typesComboBox.setEnabled(!allTypesCheckBox.isSelected());
120        loadsComboBox.setEnabled(!allLoadsCheckBox.isSelected());
121        updateLoadComboBox();
122        updateLocations();
123    }
124
125    private void updateLoadComboBox() {
126        if (allTypesCheckBox.isSelected()) {
127            carLoads.updateComboBox(loadsComboBox);
128        } else if (typesComboBox.getSelectedItem() != null) {
129            String type = (String) typesComboBox.getSelectedItem();
130            carLoads.updateComboBox(type, loadsComboBox);
131        }
132    }
133
134    private void updateLocations() {
135        String type = (String) typesComboBox.getSelectedItem();
136        String load = (String) loadsComboBox.getSelectedItem();
137        log.debug("Update locations for type ({}) load ({})", type, load);
138        locationsPanel.removeAll();
139        int x = 0;
140        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("trackSchedule")), 1, x);
141        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("receiveTypeLoad")), 2, x);
142        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("shipLoad")), 3, x);
143        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("destinationTrack")), 4, x++);
144
145        // determine if load is default empty or load
146        boolean defaultLoad = carLoads.getDefaultLoadName().equals(load) || carLoads.getDefaultEmptyName().equals(load);
147
148        for (Location location : locationManager.getLocationsByNameList()) {
149            // only spurs have schedules
150            if (!location.hasSpurs())
151                continue;
152            addItemLeft(locationsPanel, new JLabel(location.getName()), 0, x++);
153            // now look for a spur with a schedule
154            for (Track spur : location.getTracksByNameList(Track.SPUR)) {
155                Schedule sch = spur.getSchedule();
156                if (sch == null) {
157                    continue;
158                }
159                // listen for changes
160                spur.removePropertyChangeListener(this);
161                spur.addPropertyChangeListener(this);
162                sch.removePropertyChangeListener(this);
163                sch.addPropertyChangeListener(this);
164                
165                // determine if schedule is requesting car type and load
166                for (ScheduleItem si : sch.getItemsBySequenceList()) {
167                    // skip if car type doesn't carry load name
168                    if (allTypesCheckBox.isSelected() &&
169                            !allLoadsCheckBox.isSelected() &&
170                            !carLoads.containsName(si.getTypeName(), load)) {
171                        continue;
172                    }
173                    if ((allTypesCheckBox.isSelected() || si.getTypeName().equals(type)) &&
174                            (allLoadsCheckBox.isSelected() ||
175                                    si.getReceiveLoadName().equals(load) ||
176                                    si.getReceiveLoadName().equals(ScheduleItem.NONE) ||
177                                    si.getShipLoadName().equals(load) ||
178                                    (si.getShipLoadName().equals(ScheduleItem.NONE) && defaultLoad))) {
179                        // is the schedule item valid?
180                        String status = spur.checkScheduleValid();
181                        if (!status.equals(Schedule.SCHEDULE_OKAY)) {
182                            addItemLeft(locationsPanel, new JLabel("  " + status), 0, x);
183                        }
184                        addItemLeft(locationsPanel, new JLabel(spur.getName() + " (" + spur.getScheduleName() + ")"), 1,
185                                x);
186                        // create string Receive(type, delivery, road, load)
187                        String s = si.getTypeName() +
188                                ", " +
189                                si.getSetoutTrainScheduleName() +
190                                ", " +
191                                si.getRoadName() +
192                                ", " +
193                                si.getReceiveLoadName();
194                        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Receive") + " (" + s + ")"), 2, x);
195                        // create string Ship(load, pickup)
196                        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage(
197                                "Ship") + " (" + si.getShipLoadName() + ", " + si.getPickupTrainScheduleName() + ")"),
198                                3, x++);
199                        // now the destination and track
200                        if (si.getDestination() != null) {
201                            addItemLeft(locationsPanel,
202                                    new JLabel(si.getDestinationName() + " (" + si.getDestinationTrackName() + ")"), 4,
203                                    x - 1);
204                        }
205                        // report if spur can't service the selected load
206                        if (!allLoadsCheckBox.isSelected() &&
207                                si.getReceiveLoadName().equals(ScheduleItem.NONE) &&
208                                !spur.isLoadNameAndCarTypeAccepted(load, type)) {
209                            addItemLeft(locationsPanel,
210                                    new JLabel (Bundle.getMessage("spurNotTypeLoad",
211                                            spur.getName(), type, load )),
212                                    2, x++);
213                        }
214                    }
215                }
216            }
217        }
218        locationsPanel.revalidate();
219        revalidate();
220        repaint();
221    }
222
223    @Override
224    public void dispose() {
225        locationManager.removePropertyChangeListener(this);
226        carTypes.removePropertyChangeListener(this);
227        carLoads.removePropertyChangeListener(this);
228        for (Track spur : locationManager.getTracks(Track.SPUR)) {
229            Schedule sch = spur.getSchedule();
230            if (sch == null) {
231                continue;
232            }
233            spur.removePropertyChangeListener(this);
234            sch.removePropertyChangeListener(this);
235        }
236        super.dispose();
237    }
238
239    @Override
240    public void propertyChange(java.beans.PropertyChangeEvent e) {
241        log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N
242
243        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY)) {
244            carTypes.updateComboBox(typesComboBox);
245        }
246        if (e.getSource().getClass().equals(CarLoads.class)) {
247            carLoads.updateComboBox((String) typesComboBox.getSelectedItem(), loadsComboBox);
248        }
249        if (e.getSource().getClass().equals(Schedule.class) ||
250                e.getSource().getClass().equals(LocationManager.class) ||
251                e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) ||
252                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) {
253            updateLocations();
254        }
255    }
256
257    private final static Logger log = LoggerFactory.getLogger(SchedulesByLoadFrame.class);
258
259}