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