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