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