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, 2026 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 toolMenu.addSeparator(); 098 toolMenu.add(new PrintSchedulesByTypeAndLoadAction(true, this)); 099 toolMenu.add(new PrintSchedulesByTypeAndLoadAction(false, this)); 100 menuBar.add(toolMenu); 101 setJMenuBar(menuBar); 102 addHelpMenu("package.jmri.jmrit.operations.Operations_ShowSchedulesByCarTypeAndLoad", true); // NOI18N 103 104 // select first item to load contents 105 typesComboBox.setSelectedIndex(0); 106 107 initMinimumSize(new Dimension(Control.panelWidth1025, Control.panelHeight250)); 108 } 109 110 @Override 111 public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) { 112 if (ae.getSource() == typesComboBox) { 113 updateLoadComboBox(); 114 } 115 if (ae.getSource() == loadsComboBox) { 116 updateLocations(); 117 } 118 119 } 120 121 @Override 122 public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) { 123 typesComboBox.setEnabled(!allTypesCheckBox.isSelected()); 124 loadsComboBox.setEnabled(!allLoadsCheckBox.isSelected()); 125 updateLoadComboBox(); 126 updateLocations(); 127 } 128 129 private void updateLoadComboBox() { 130 if (allTypesCheckBox.isSelected()) { 131 carLoads.updateComboBox(loadsComboBox); 132 } else if (typesComboBox.getSelectedItem() != null) { 133 String type = (String) typesComboBox.getSelectedItem(); 134 carLoads.updateComboBox(type, loadsComboBox); 135 } 136 } 137 138 private void updateLocations() { 139 locationsPanel.removeAll(); 140 String type = (String) typesComboBox.getSelectedItem(); 141 String load = (String) loadsComboBox.getSelectedItem(); 142 // determine if load is default empty or load 143 boolean defaultLoad = carLoads.getDefaultLoadName().equals(load) || carLoads.getDefaultEmptyName().equals(load); 144 log.debug("Update locations for type ({}) load ({})", type, load); 145 146 // build header 147 int row = 0; 148 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Spur")), 1, row); 149 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Schedule")), 2, row); 150 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Id")), 3, row); 151 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("ScheduleMode")), 4, row); 152 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Type")), 5, row); 153 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Random")), 6, row); 154 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Delivery")), 7, row); 155 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Road")), 8, row); 156 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Receive")), 9, row); 157 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Ship")), 10, row); 158 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Destination")), 11, row); 159 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Track")), 12, row); 160 addItemLeft(locationsPanel, new JLabel(Bundle.getMessage("Pickup")), 13, row++); 161 162 // now load data 163 for (Location location : locationManager.getLocationsByNameList()) { 164 // only spurs have schedules 165 if (!location.hasSchedules()) 166 continue; 167 addItemLeft(locationsPanel, new JLabel(location.getName()), 0, row++); 168 // now look for a spur with a schedule 169 for (Track spur : location.getTracksByNameList(Track.SPUR)) { 170 Schedule sch = spur.getSchedule(); 171 if (sch == null) { 172 continue; 173 } 174 // listen for changes 175 spur.removePropertyChangeListener(this); 176 spur.addPropertyChangeListener(this); 177 sch.removePropertyChangeListener(this); 178 sch.addPropertyChangeListener(this); 179 180 // determine if schedule is requesting car type and load 181 for (ScheduleItem si : sch.getItemsBySequenceList()) { 182 // skip if car type doesn't carry load name 183 if (allTypesCheckBox.isSelected() && 184 !allLoadsCheckBox.isSelected() && 185 !carLoads.containsName(si.getTypeName(), load)) { 186 continue; 187 } 188 if ((allTypesCheckBox.isSelected() || si.getTypeName().equals(type)) && 189 (allLoadsCheckBox.isSelected() || 190 si.getReceiveLoadName().equals(load) || 191 si.getReceiveLoadName().equals(ScheduleItem.NONE) || 192 si.getShipLoadName().equals(load) || 193 (si.getShipLoadName().equals(ScheduleItem.NONE) && defaultLoad))) { 194 // is the schedule item valid? 195 String status = spur.checkScheduleValid(); 196 if (!status.equals(Schedule.SCHEDULE_OKAY)) { 197 JLabel errorSchedule = new JLabel(" " + status); 198 errorSchedule.setForeground(Color.RED); 199 addItemLeft(locationsPanel, errorSchedule, 0, row); 200 } 201 addItemLeft(locationsPanel, new JLabel(spur.getName()), 1, row); 202 addItemLeft(locationsPanel, new JLabel(spur.getScheduleName()), 2, row); 203 addItemLeft(locationsPanel, new JLabel(si.getId()), 3, row); 204 addItemLeft(locationsPanel, new JLabel(spur.getScheduleModeName()), 4, row); 205 addItemLeft(locationsPanel, new JLabel(si.getTypeName()), 5, row); 206 addItemLeft(locationsPanel, new JLabel(si.getRandom()), 6, row); 207 addItemLeft(locationsPanel, new JLabel(si.getSetoutTrainScheduleName()), 7, row); 208 addItemLeft(locationsPanel, new JLabel(si.getRoadName()), 8, row); 209 // report if spur can't service the selected load 210 if (!allLoadsCheckBox.isSelected() && 211 si.getReceiveLoadName().equals(ScheduleItem.NONE) && 212 !spur.isLoadNameAndCarTypeAccepted(load, type)) { 213 JLabel warnLoad = 214 new JLabel(Bundle.getMessage("spurNotTypeLoad", spur.getName(), type, load)); 215 warnLoad.setForeground(Color.BLUE); 216 addItemLeft(locationsPanel, warnLoad, 9, row); 217 } else { 218 addItemLeft(locationsPanel, new JLabel(si.getReceiveLoadName()), 9, row); 219 } 220 addItemLeft(locationsPanel, new JLabel(si.getShipLoadName()), 10, row); 221 // now the destination and track 222 if (si.getDestination() != null) { 223 addItemLeft(locationsPanel, 224 new JLabel(si.getDestinationName()), 11, row); 225 addItemLeft(locationsPanel, 226 new JLabel(si.getDestinationTrackName()), 12, row); 227 } 228 addItemLeft(locationsPanel, new JLabel(si.getPickupTrainScheduleName()), 13, row++); 229 } 230 } 231 } 232 } 233 locationsPanel.revalidate(); 234 revalidate(); 235 repaint(); 236 } 237 238 public String getCarType() { 239 if (allTypesCheckBox.isSelected()) { 240 return null; 241 } 242 return (String) typesComboBox.getSelectedItem(); 243 } 244 245 public String getCarLoad() { 246 if (allLoadsCheckBox.isSelected()) { 247 return null; 248 } 249 return (String) loadsComboBox.getSelectedItem(); 250 } 251 252 @Override 253 public void dispose() { 254 locationManager.removePropertyChangeListener(this); 255 carTypes.removePropertyChangeListener(this); 256 carLoads.removePropertyChangeListener(this); 257 for (Track spur : locationManager.getTracks(Track.SPUR)) { 258 Schedule sch = spur.getSchedule(); 259 if (sch == null) { 260 continue; 261 } 262 spur.removePropertyChangeListener(this); 263 sch.removePropertyChangeListener(this); 264 } 265 super.dispose(); 266 } 267 268 @Override 269 public void propertyChange(java.beans.PropertyChangeEvent e) { 270 log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N 271 272 if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY)) { 273 carTypes.updateComboBox(typesComboBox); 274 } 275 if (e.getSource().getClass().equals(CarLoads.class)) { 276 carLoads.updateComboBox((String) typesComboBox.getSelectedItem(), loadsComboBox); 277 } 278 if (e.getSource().getClass().equals(Schedule.class) || 279 e.getSource().getClass().equals(LocationManager.class) || 280 e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) || 281 e.getPropertyName().equals(Track.ROADS_CHANGED_PROPERTY) || 282 e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) { 283 updateLocations(); 284 } 285 } 286 287 private static final Logger log = LoggerFactory.getLogger(SchedulesByLoadFrame.class); 288 289}