001package jmri.jmrit.operations.locations; 002 003import java.awt.Dimension; 004import java.awt.event.ActionEvent; 005import java.util.List; 006 007import javax.swing.*; 008 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import jmri.InstanceManager; 013import jmri.jmrit.operations.CommonConductorYardmasterPanel; 014import jmri.jmrit.operations.rollingstock.RollingStock; 015import jmri.jmrit.operations.rollingstock.cars.Car; 016import jmri.jmrit.operations.routes.RouteLocation; 017import jmri.jmrit.operations.setup.Control; 018import jmri.jmrit.operations.setup.Setup; 019import jmri.jmrit.operations.trains.*; 020 021/** 022 * Yardmaster Frame. Shows work at one location. 023 * 024 * @author Dan Boudreau Copyright (C) 2013 025 * 026 */ 027public class YardmasterPanel extends CommonConductorYardmasterPanel { 028 029 int _visitNumber = 1; 030 031 // combo boxes 032 JComboBox<Train> trainComboBox = new JComboBox<>(); 033 JComboBox<Integer> trainVisitComboBox = new JComboBox<>(); 034 035 // buttons 036 JButton nextButton = new JButton(Bundle.getMessage("Next")); 037 038 // panels 039 JPanel pTrainVisit = new JPanel(); 040 041 public YardmasterPanel() { 042 this(null); 043 } 044 045 public YardmasterPanel(Location location) { 046 super(); 047 048 _location = location; 049 050 // row 2 051 JPanel pRow2 = new JPanel(); 052 pRow2.setLayout(new BoxLayout(pRow2, BoxLayout.X_AXIS)); 053 054 pRow2.add(pLocationName); // row 2a (location name) 055 pRow2.add(pRailRoadName); // row 2b (railroad name) 056 057 // row 6 058 JPanel pRow6 = new JPanel(); 059 pRow6.setLayout(new BoxLayout(pRow6, BoxLayout.X_AXIS)); 060 061 // row 6a (train name) 062 JPanel pTrainName = new JPanel(); 063 pTrainName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Train"))); 064 pTrainName.add(trainComboBox); 065 // add next button for web server 066 pTrainName.add(nextButton); 067 068 // row 6b (train visit) 069 pTrainVisit.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Visit"))); 070 padComboBox(trainVisitComboBox, 2); 071 pTrainVisit.add(trainVisitComboBox); 072 073 pRow6.add(pTrainName); 074 pRow6.add(pTrainVisit); 075 pRow6.add(pTrainDescription); // row 6c (train description) 076 077 pButtons.setMaximumSize(new Dimension(2000, 200)); 078 079 add(pRow2); 080 add(pRow6); 081 add(textLocationCommentPane); 082 add(textSwitchListCommentPane); 083 add(textTrainCommentPane); 084 add(textTrainRouteCommentPane); 085 add(textTrainRouteLocationCommentPane); 086 add(pTrackComments); 087 add(textTrainStatusPane); 088 add(locoPane); 089 add(pWorkPanes); 090 add(movePane); 091 add(pStatus); 092 add(pButtons); 093 094 if (_location != null) { 095 textLocationName.setText(_location.getName()); 096 loadLocationComment(_location); 097 loadLocationSwitchListComment(_location); 098 updateTrainsComboBox(); 099 } 100 101 update(); 102 103 addComboBoxAction(trainComboBox); 104 addComboBoxAction(trainVisitComboBox); 105 106 addButtonAction(nextButton); 107 108 // listen for trains 109 addTrainListeners(); 110 } 111 112 // Select, Clear, and Set Buttons 113 @Override 114 public void buttonActionPerformed(ActionEvent ae) { 115 if (ae.getSource() == nextButton) { 116 nextButtonAction(); 117 } 118 super.buttonActionPerformed(ae); 119 } 120 121 private void nextButtonAction() { 122 log.debug("next button activated"); 123 if (trainComboBox.getItemCount() > 1) { 124 if (pTrainVisit.isVisible()) { 125 int index = trainVisitComboBox.getSelectedIndex() + 1; 126 if (index < trainVisitComboBox.getItemCount()) { 127 trainVisitComboBox.setSelectedIndex(index); 128 return; // done 129 } 130 } 131 int index = trainComboBox.getSelectedIndex(); 132 // index = -1 if first item (null) in trainComboBox 133 if (index == -1) { 134 index = 1; 135 } else { 136 index++; 137 } 138 if (index >= trainComboBox.getItemCount()) { 139 index = 0; 140 } 141 trainComboBox.setSelectedIndex(index); 142 } 143 } 144 145 // Select Train and Visit 146 @Override 147 protected void comboBoxActionPerformed(ActionEvent ae) { 148 // made the combo box not visible during updates, so ignore if not visible 149 if (ae.getSource() == trainComboBox && trainComboBox.isVisible()) { 150 _train = null; 151 if (trainComboBox.getSelectedItem() != null) { 152 _train = (Train) trainComboBox.getSelectedItem(); 153 _visitNumber = 1; 154 } 155 clearAndUpdate(); 156 } 157 // made the combo box not visible during updates, so ignore if not visible 158 if (ae.getSource() == trainVisitComboBox && trainVisitComboBox.isVisible()) { 159 if (trainVisitComboBox.getSelectedItem() != null) { 160 _visitNumber = (Integer) trainVisitComboBox.getSelectedItem(); 161 clearAndUpdate(); 162 } 163 } 164 } 165 166 @Override 167 protected void update() { 168 log.debug("queue update"); 169 // use invokeLater to prevent deadlock 170 SwingUtilities.invokeLater(() -> { 171 log.debug("update, setMode: {}", isSetMode); 172 initialize(); 173 174 // turn everything off and re-enable if needed 175 pButtons.setVisible(false); 176 pTrainVisit.setVisible(false); 177 trainVisitComboBox.setVisible(false); // Use visible as a flag to ignore updates 178 textTrainCommentPane.setVisible(false); 179 textTrainRouteCommentPane.setVisible(false); 180 textTrainRouteLocationCommentPane.setVisible(false); 181 textTrainStatusPane.setVisible(false); 182 183 textTrainDescription.setText(""); 184 textStatus.setText(""); 185 186 if (_train != null && _train.isBuilt() && _train.getRoute() != null) { 187 pButtons.setVisible(true); 188 189 loadTrainDescription(); 190 loadTrainComment(); 191 loadRouteComment(); 192 loadRailroadName(); 193 194 // determine how many times this train visits this location and if it is the 195 // last stop 196 RouteLocation rl = null; 197 List<RouteLocation> routeList = _train.getRoute().getLocationsBySequenceList(); 198 int visitNumber = 0; 199 for (int i = 0; i < routeList.size(); i++) { 200 if (routeList.get(i).getSplitName().equals(_location.getSplitName())) { 201 visitNumber++; 202 if (visitNumber == _visitNumber) { 203 rl = routeList.get(i); 204 } 205 } 206 } 207 208 if (rl != null) { 209 // update visit numbers 210 if (visitNumber > 1) { 211 trainVisitComboBox.removeAllItems(); // this fires an action change! 212 for (int i = 0; i < visitNumber; i++) { 213 trainVisitComboBox.addItem(i + 1); 214 } 215 trainVisitComboBox.setSelectedItem(_visitNumber); 216 trainVisitComboBox.setVisible(true); // now pay attention to changes 217 pTrainVisit.setVisible(true); // show the visit panel 218 } 219 220 if (Setup.isSwitchListRouteLocationCommentEnabled()) { 221 loadRouteLocationComment(rl); 222 } 223 textLocationName.setText(rl.getLocation().getName()); // show name including hyphen and number 224 225 updateTrackComments(rl, !IS_MANIFEST); 226 227 String msg = TrainCommon.getSwitchListTrainStatus(_train, rl); 228 textTrainStatusPane.setText(msg); 229 textTrainStatusPane.setVisible(!msg.isBlank()); 230 231 // check for locos 232 updateLocoPanes(rl); 233 234 // now update the car pick ups and set outs 235 blockCars(rl, !IS_MANIFEST); 236 237 textStatus.setText(getStatus(rl, !IS_MANIFEST)); 238 } 239 updateComplete(); 240 } 241 }); 242 } 243 244 private void updateTrainsComboBox() { 245 Object selectedItem = trainComboBox.getSelectedItem(); 246 trainComboBox.setVisible(false); // used as a flag to ignore updates 247 trainComboBox.removeAllItems(); 248 trainComboBox.addItem(null); 249 if (_location != null) { 250 List<Train> trains = trainManager.getTrainsArrivingThisLocationList(_location); 251 trains.stream().filter((train) -> (TrainCommon.isThereWorkAtLocation(train, _location))) 252 .forEach((train) -> { 253 trainComboBox.addItem(train); 254 }); 255 } 256 if (selectedItem != null) { 257 trainComboBox.setSelectedItem(selectedItem); 258 } 259 padComboBox(trainComboBox); 260 trainComboBox.setVisible(true); 261 } 262 263 private void addTrainListeners() { 264 log.debug("Adding train listerners"); 265 List<Train> trains = InstanceManager.getDefault(TrainManager.class).getTrainsByIdList(); 266 trains.stream().forEach((train) -> { 267 train.addPropertyChangeListener(this); 268 }); 269 // listen for new trains being added 270 InstanceManager.getDefault(TrainManager.class).addPropertyChangeListener(this); 271 } 272 273 private void removeTrainListeners() { 274 log.debug("Removing train listerners"); 275 List<Train> trains = InstanceManager.getDefault(TrainManager.class).getTrainsByIdList(); 276 trains.stream().forEach((train) -> { 277 train.removePropertyChangeListener(this); 278 }); 279 InstanceManager.getDefault(TrainManager.class).removePropertyChangeListener(this); 280 } 281 282 @Override 283 public void dispose() { 284 removeTrainListeners(); 285 removePropertyChangeListerners(); 286 } 287 288 @Override 289 public void propertyChange(java.beans.PropertyChangeEvent e) { 290 if (Control.SHOW_PROPERTY) { 291 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), 292 e.getNewValue()); 293 } 294 if ((e.getPropertyName().equals(RollingStock.ROUTE_LOCATION_CHANGED_PROPERTY) && e.getNewValue() == null) || 295 (e.getPropertyName().equals(RollingStock.ROUTE_DESTINATION_CHANGED_PROPERTY) && 296 e.getNewValue() == null) || 297 e.getPropertyName().equals(RollingStock.TRAIN_CHANGED_PROPERTY) || 298 e.getPropertyName().equals(Train.TRAIN_MODIFIED_CHANGED_PROPERTY)) { 299 // remove car from list 300 if (e.getSource().getClass().equals(Car.class)) { 301 Car car = (Car) e.getSource(); 302 removeCarFromList(car); 303 } 304 update(); 305 } 306 if (e.getPropertyName().equals(Train.BUILT_CHANGED_PROPERTY)) { 307 updateTrainsComboBox(); 308 } 309 if (e.getPropertyName().equals(Train.TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY) && e.getSource() == _train) { 310 update(); 311 } 312 } 313 314 private final static Logger log = LoggerFactory.getLogger(YardmasterPanel.class); 315}