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}