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 the staging tracks and spurs with schedules. Lists staging
024 * tracks that will service the car type and the spurs that will accept the
025 * selected load name.
026 *
027 * @author Dan Boudreau Copyright (C) 2023
028 */
029public class SchedulesAndStagingFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
030
031    // managers'
032    LocationManager locationManager = InstanceManager.getDefault(LocationManager.class);
033    CarLoads carLoads = InstanceManager.getDefault(CarLoads.class);
034    CarTypes carTypes = InstanceManager.getDefault(CarTypes.class);
035
036    // combo box
037    JComboBox<String> typesComboBox = carTypes.getComboBox();
038    JComboBox<String> loadsComboBox = new JComboBox<>();
039
040    // panels
041    JPanel locationsPanel;
042
043    // checkbox
044    JCheckBox generatedLoadsCheckBox = new JCheckBox(Bundle.getMessage("generatedLoads"));
045    JCheckBox allLoadsCheckBox = new JCheckBox(Bundle.getMessage("allLoads"));
046
047    public SchedulesAndStagingFrame() {
048        super(Bundle.getMessage("MenuItemStagingSchedules"));
049
050        // the following code sets the frame's initial state
051        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
052
053        // load the panel
054        JPanel p1 = new JPanel();
055        p1.setMaximumSize(new Dimension(2000, 200));
056        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
057
058        JPanel type = new JPanel();
059        type.setLayout(new GridBagLayout());
060        type.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
061        addItem(type, typesComboBox, 0, 0);
062
063        JPanel load = new JPanel();
064        load.setLayout(new GridBagLayout());
065        load.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Load")));
066        addItem(load, loadsComboBox, 0, 0);
067        addItem(load, generatedLoadsCheckBox, 1, 0);
068        addItem(load, allLoadsCheckBox, 2, 0);
069
070        p1.add(type);
071        p1.add(load);
072
073        locationsPanel = new JPanel();
074        locationsPanel.setLayout(new GridBagLayout());
075        JScrollPane locationsPane = new JScrollPane(locationsPanel);
076        locationsPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
077        locationsPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Staging")));
078
079        getContentPane().add(p1);
080        getContentPane().add(locationsPane);
081
082        addComboBoxAction(typesComboBox);
083        addComboBoxAction(loadsComboBox);
084
085        generatedLoadsCheckBox.setSelected(true);
086        generatedLoadsCheckBox.setToolTipText(Bundle.getMessage("generatedLoadsTip"));
087
088        addCheckBoxAction(generatedLoadsCheckBox);
089        addCheckBoxAction(allLoadsCheckBox);
090
091        // property changes
092        locationManager.addPropertyChangeListener(this);
093        carTypes.addPropertyChangeListener(this);
094        carLoads.addPropertyChangeListener(this);
095
096        // build menu
097        JMenuBar menuBar = new JMenuBar();
098        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
099        toolMenu.add(new CarLoadEditFrameAction());
100        toolMenu.add(new PrintCarLoadsAction(true));
101        toolMenu.add(new PrintCarLoadsAction(false));
102        menuBar.add(toolMenu);
103        setJMenuBar(menuBar);
104        addHelpMenu("package.jmri.jmrit.operations.Operations_ShowStagingAndSchedulesByCarTypeAndLoad", true); // NOI18N
105
106        // select first item to load contents
107        typesComboBox.setSelectedIndex(0);
108
109        initMinimumSize(new Dimension(Control.panelWidth700, Control.panelHeight250));
110    }
111
112    @Override
113    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
114        if (ae.getSource() == typesComboBox) {
115            updateLoadComboBox();
116        }
117        if (ae.getSource() == loadsComboBox) {
118            updateLocations();
119        }
120
121    }
122
123    @Override
124    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
125        loadsComboBox.setEnabled(!allLoadsCheckBox.isSelected());
126        updateLocations();
127    }
128
129    private void updateLoadComboBox() {
130        String type = (String) typesComboBox.getSelectedItem();
131        carLoads.updateComboBox(type, loadsComboBox);
132    }
133
134    int x;
135
136    private void updateLocations() {
137        locationsPanel.removeAll();
138        // create header
139        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Track")), 1, 0);
140        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("CarLoadOptions")), 2, 0);
141        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("ShipLoadOption")), 3, 0);
142        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Load")), 4, 0);
143        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("destinationTrack")), 5, 0);
144        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("LoadOption")), 6, 0);
145        addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Schedule")), 7, 0);
146
147        x = 1;
148        for (Location location : locationManager.getLocationsByNameList()) {
149            if (!location.isStaging())
150                continue;
151            location.removePropertyChangeListener(this);
152            location.addPropertyChangeListener(this);
153            addItemLeft(locationsPanel, new JLabel(location.getName()), 0, x++);
154            // list staging tracks
155            for (Track track : location.getTracksByNameList(Track.STAGING)) {
156                // listen for changes
157                track.removePropertyChangeListener(this);
158                track.addPropertyChangeListener(this);
159                addItemLeft(locationsPanel, new JLabel(track.getName()), 1, x);
160                addItemLeft(locationsPanel, new JLabel(getTrackCarLoadOptions(track)), 2, x);
161                addItemLeft(locationsPanel, new JLabel(track.getShipLoadOptionString()), 3, x);
162                listSpurs(track);
163                x++;
164            }
165        }
166        locationsPanel.revalidate();
167        revalidate();
168        repaint();
169    }
170
171    private String getTrackCarLoadOptions(Track track) {
172        StringBuffer options = new StringBuffer();
173        if (track.isLoadSwapEnabled()) {
174            options.append(Bundle.getMessage("ABV_SwapDefaultLoads") + ", ");
175        }
176        if (track.isLoadEmptyEnabled()) {
177            options.append(Bundle.getMessage("ABV_EmptyDefaultLoads") + ", ");
178        }
179        if (track.isRemoveCustomLoadsEnabled()) {
180            options.append(Bundle.getMessage("ABV_EmptyCustomLoads") + ", ");
181        }
182        if (track.isAddCustomLoadsEnabled()) {
183            options.append(Bundle.getMessage("ABV_GenerateCustomLoad") + ", ");
184        }
185        if (track.isAddCustomLoadsAnySpurEnabled()) {
186            options.append(Bundle.getMessage("ABV_GenerateCustomLoadAnySpur") + ", ");
187        }
188        if (track.isAddCustomLoadsAnyStagingTrackEnabled()) {
189            options.append(Bundle.getMessage("ABV_GereateCustomLoadStaging"));
190        }
191        return options.toString();
192    }
193
194    /*
195     * List spurs that this staging track can service
196     */
197    private void listSpurs(Track track) {
198        // is this staging track configured to generate custom loads?
199        if (!track.isAddCustomLoadsAnySpurEnabled() && !track.isAddCustomLoadsEnabled()) {
200            return;
201        }
202        String type = (String) typesComboBox.getSelectedItem();
203        if (!track.isTypeNameAccepted(type)) {
204            return;
205        }
206        if (allLoadsCheckBox.isSelected()) {
207            for (String load : carLoads.getNames(type)) {
208                listSpurs(track, type, load);
209            }
210        } else {
211            String load = (String) loadsComboBox.getSelectedItem();
212            listSpurs(track, type, load);
213        }
214    }
215
216    private void listSpurs(Track track, String type, String load) {
217        if (load == null || !track.isLoadNameAndCarTypeShipped(load, type)) {
218            return;
219        }
220        // ignore default empty and load names
221        if (generatedLoadsCheckBox.isSelected() &&
222                (load.equals(carLoads.getDefaultEmptyName()) || load.equals(carLoads.getDefaultLoadName()))) {
223            return;
224        }
225        // now list all of the spurs with schedules for this type and load
226        for (Location location : locationManager.getLocationsByNameList()) {
227            // only spurs have schedules
228            if (!location.hasSpurs())
229                continue;
230            // find spurs with a schedule
231            for (Track spur : location.getTracksByNameList(Track.SPUR)) {
232                Schedule sch = spur.getSchedule();
233                if (sch == null) {
234                    continue;
235                }
236                // listen for changes
237                spur.removePropertyChangeListener(this);
238                spur.addPropertyChangeListener(this);
239                sch.removePropertyChangeListener(this);
240                sch.addPropertyChangeListener(this);
241                // determine if schedule is requesting car type and load
242                for (ScheduleItem si : sch.getItemsBySequenceList()) {
243                    if (spur.isLoadNameAccepted(load) &&
244                            si.getTypeName().equals(type) &&
245                            (si.getReceiveLoadName().equals(load) ||
246                                    (si.getReceiveLoadName().equals(ScheduleItem.NONE) &&
247                                            !generatedLoadsCheckBox.isSelected()))) {
248                        addItemLeft(locationsPanel, new JLabel(load), 4, x);
249                        addItemLeft(locationsPanel, new JLabel(location.getName() + " (" + spur.getName() + ")"), 5, x);
250                        addItemLeft(locationsPanel, new JLabel(spur.getLoadOptionString()), 6, x);
251                        addItemLeft(locationsPanel, new JLabel(sch.getName() + " " + si.getId()), 7, x++);
252                    }
253                }
254            }
255        }
256    }
257
258    @Override
259    public void dispose() {
260        locationManager.removePropertyChangeListener(this);
261        carTypes.removePropertyChangeListener(this);
262        carLoads.removePropertyChangeListener(this);
263        for (Location location : locationManager.getLocationsByNameList()) {
264            location.removePropertyChangeListener(this);
265        }
266        for (Track spur : locationManager.getTracks(Track.SPUR)) {
267            Schedule sch = spur.getSchedule();
268            if (sch == null) {
269                continue;
270            }
271            spur.removePropertyChangeListener(this);
272            sch.removePropertyChangeListener(this);
273        }
274        super.dispose();
275    }
276
277    @Override
278    public void propertyChange(java.beans.PropertyChangeEvent e) {
279        log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N
280
281        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY)) {
282            carTypes.updateComboBox(typesComboBox);
283        }
284        if (e.getSource().getClass().equals(CarLoads.class)) {
285            carLoads.updateComboBox((String) typesComboBox.getSelectedItem(), loadsComboBox);
286        }
287        if (e.getSource().getClass().equals(Schedule.class) ||
288                e.getSource().getClass().equals(LocationManager.class) ||
289                e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) ||
290                e.getPropertyName().equals(Track.LOAD_OPTIONS_CHANGED_PROPERTY) ||
291                e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) ||
292                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) {
293            updateLocations();
294        }
295    }
296
297    private final static Logger log = LoggerFactory.getLogger(SchedulesAndStagingFrame.class);
298
299}