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(locoPane); 088 add(pWorkPanes); 089 add(movePane); 090 add(pStatus); 091 add(pButtons); 092 093 if (_location != null) { 094 textLocationName.setText(_location.getName()); 095 loadLocationComment(_location); 096 loadLocationSwitchListComment(_location); 097 updateTrainsComboBox(); 098 } 099 100 update(); 101 102 addComboBoxAction(trainComboBox); 103 addComboBoxAction(trainVisitComboBox); 104 105 addButtonAction(nextButton); 106 107 // listen for trains being built 108 addTrainListeners(); 109 } 110 111 // Select, Clear, and Set Buttons 112 @Override 113 public void buttonActionPerformed(ActionEvent ae) { 114 if (ae.getSource() == nextButton) { 115 nextButtonAction(); 116 } 117 super.buttonActionPerformed(ae); 118 } 119 120 private void nextButtonAction() { 121 log.debug("next button activated"); 122 if (trainComboBox.getItemCount() > 1) { 123 if (pTrainVisit.isVisible()) { 124 int index = trainVisitComboBox.getSelectedIndex() + 1; 125 if (index < trainVisitComboBox.getItemCount()) { 126 trainVisitComboBox.setSelectedIndex(index); 127 return; // done 128 } 129 } 130 int index = trainComboBox.getSelectedIndex(); 131 // index = -1 if first item (null) in trainComboBox 132 if (index == -1) { 133 index = 1; 134 } else { 135 index++; 136 } 137 if (index >= trainComboBox.getItemCount()) { 138 index = 0; 139 } 140 trainComboBox.setSelectedIndex(index); 141 } 142 } 143 144 // Select Train and Visit 145 @Override 146 protected void comboBoxActionPerformed(ActionEvent ae) { 147 // made the combo box not visible during updates, so ignore if not visible 148 if (ae.getSource() == trainComboBox && trainComboBox.isVisible()) { 149 _train = null; 150 if (trainComboBox.getSelectedItem() != null) { 151 _train = (Train) trainComboBox.getSelectedItem(); 152 _visitNumber = 1; 153 } 154 clearAndUpdate(); 155 } 156 // made the combo box not visible during updates, so ignore if not visible 157 if (ae.getSource() == trainVisitComboBox && trainVisitComboBox.isVisible()) { 158 if (trainVisitComboBox.getSelectedItem() != null) { 159 _visitNumber = (Integer) trainVisitComboBox.getSelectedItem(); 160 clearAndUpdate(); 161 } 162 } 163 } 164 165 @Override 166 protected void update() { 167 log.debug("queue update"); 168 // use invokeLater to prevent deadlock 169 SwingUtilities.invokeLater(() -> { 170 log.debug("update, setMode: {}", isSetMode); 171 initialize(); 172 173 // turn everything off and re-enable if needed 174 pButtons.setVisible(false); 175 pTrainVisit.setVisible(false); 176 trainVisitComboBox.setVisible(false); // Use visible as a flag to ignore updates 177 textTrainCommentPane.setVisible(false); 178 textTrainRouteCommentPane.setVisible(false); 179 textTrainRouteLocationCommentPane.setVisible(false); 180 181 textTrainDescription.setText(""); 182 textStatus.setText(""); 183 184 if (_train != null && _train.getRoute() != null) { 185 pButtons.setVisible(true); 186 187 loadTrainDescription(); 188 loadTrainComment(); 189 loadRouteComment(); 190 loadRailroadName(); 191 192 // determine how many times this train visits this location and if it is the 193 // last stop 194 RouteLocation rl = null; 195 List<RouteLocation> routeList = _train.getRoute().getLocationsBySequenceList(); 196 int visitNumber = 0; 197 for (int i = 0; i < routeList.size(); i++) { 198 if (routeList.get(i).getSplitName().equals(_location.getSplitName())) { 199 visitNumber++; 200 if (visitNumber == _visitNumber) { 201 rl = routeList.get(i); 202 } 203 } 204 } 205 206 if (rl != null) { 207 // update visit numbers 208 if (visitNumber > 1) { 209 trainVisitComboBox.removeAllItems(); // this fires an action change! 210 for (int i = 0; i < visitNumber; i++) { 211 trainVisitComboBox.addItem(i + 1); 212 } 213 trainVisitComboBox.setSelectedItem(_visitNumber); 214 trainVisitComboBox.setVisible(true); // now pay attention to changes 215 pTrainVisit.setVisible(true); // show the visit panel 216 } 217 218 if (Setup.isSwitchListRouteLocationCommentEnabled()) { 219 loadRouteLocationComment(rl); 220 } 221 textLocationName.setText(rl.getLocation().getName()); // show name including hyphen and number 222 223 updateTrackComments(rl, !IS_MANIFEST); 224 225 // check for locos 226 updateLocoPanes(rl); 227 228 // now update the car pick ups and set outs 229 blockCars(rl, !IS_MANIFEST); 230 231 textStatus.setText(getStatus(rl, !IS_MANIFEST)); 232 } 233 updateComplete(); 234 } 235 }); 236 } 237 238 private void updateTrainsComboBox() { 239 Object selectedItem = trainComboBox.getSelectedItem(); 240 trainComboBox.setVisible(false); // used as a flag to ignore updates 241 trainComboBox.removeAllItems(); 242 trainComboBox.addItem(null); 243 if (_location != null) { 244 List<Train> trains = trainManager.getTrainsArrivingThisLocationList(_location); 245 trains.stream().filter((train) -> (TrainCommon.isThereWorkAtLocation(train, _location))) 246 .forEach((train) -> { 247 trainComboBox.addItem(train); 248 }); 249 } 250 if (selectedItem != null) { 251 trainComboBox.setSelectedItem(selectedItem); 252 } 253 padComboBox(trainComboBox); 254 trainComboBox.setVisible(true); 255 } 256 257 private void addTrainListeners() { 258 log.debug("Adding train listerners"); 259 List<Train> trains = InstanceManager.getDefault(TrainManager.class).getTrainsByIdList(); 260 trains.stream().forEach((train) -> { 261 train.addPropertyChangeListener(this); 262 }); 263 // listen for new trains being added 264 InstanceManager.getDefault(TrainManager.class).addPropertyChangeListener(this); 265 } 266 267 private void removeTrainListeners() { 268 log.debug("Removing train listerners"); 269 List<Train> trains = InstanceManager.getDefault(TrainManager.class).getTrainsByIdList(); 270 trains.stream().forEach((train) -> { 271 train.removePropertyChangeListener(this); 272 }); 273 InstanceManager.getDefault(TrainManager.class).removePropertyChangeListener(this); 274 } 275 276 @Override 277 public void dispose() { 278 removeTrainListeners(); 279 removePropertyChangeListerners(); 280 } 281 282 @Override 283 public void propertyChange(java.beans.PropertyChangeEvent e) { 284 if (Control.SHOW_PROPERTY) { 285 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), 286 e.getNewValue()); 287 } 288 if ((e.getPropertyName().equals(RollingStock.ROUTE_LOCATION_CHANGED_PROPERTY) && e.getNewValue() == null) || 289 (e.getPropertyName().equals(RollingStock.ROUTE_DESTINATION_CHANGED_PROPERTY) && 290 e.getNewValue() == null) || 291 e.getPropertyName().equals(RollingStock.TRAIN_CHANGED_PROPERTY) || 292 e.getPropertyName().equals(Train.TRAIN_MODIFIED_CHANGED_PROPERTY)) { 293 // remove car from list 294 if (e.getSource().getClass().equals(Car.class)) { 295 Car car = (Car) e.getSource(); 296 removeCarFromList(car); 297 } 298 update(); 299 } 300 if (e.getPropertyName().equals(Train.BUILT_CHANGED_PROPERTY)) { 301 updateTrainsComboBox(); 302 } 303 } 304 305 private final static Logger log = LoggerFactory.getLogger(YardmasterPanel.class); 306}