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