001package jmri.jmrit.operations.locations; 002 003import java.awt.Color; 004import java.awt.Dimension; 005import java.awt.event.ActionEvent; 006import java.text.MessageFormat; 007import java.util.ArrayList; 008import java.util.List; 009 010import javax.swing.*; 011 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015import jmri.InstanceManager; 016import jmri.jmrit.operations.CommonConductorYardmasterPanel; 017import jmri.jmrit.operations.rollingstock.RollingStock; 018import jmri.jmrit.operations.rollingstock.cars.*; 019import jmri.jmrit.operations.rollingstock.engines.Engine; 020import jmri.jmrit.operations.routes.RouteLocation; 021import jmri.jmrit.operations.setup.Control; 022import jmri.jmrit.operations.setup.Setup; 023import jmri.jmrit.operations.trains.Train; 024import jmri.jmrit.operations.trains.TrainCommon; 025import jmri.jmrit.operations.trains.TrainSwitchListText; 026 027/** 028 * Yardmaster frame by track. Shows work at one location listed by track. 029 * 030 * @author Dan Boudreau Copyright (C) 2015 031 * 032 */ 033public class YardmasterByTrackPanel extends CommonConductorYardmasterPanel { 034 035 protected Track _track = null; 036 037 // text panes 038 JTextPane textTrackCommentPane = new JTextPane(); 039 040 // combo boxes 041 JComboBox<Track> trackComboBox = new JComboBox<>(); 042 043 // buttons 044 JButton nextButton = new JButton(Bundle.getMessage("Next")); 045 046 // panel 047 JPanel pTrack = new JPanel(); 048 JScrollPane pTrackPane; 049 050 public YardmasterByTrackPanel() { 051 this(null); 052 } 053 054 public YardmasterByTrackPanel(Location location) { 055 super(); 056 057 // this window doesn't use the set button 058 modifyButton.setVisible(false); 059 060 _location = location; 061 062 textTrackCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrackComment"))); 063 textTrackCommentPane.setBackground(null); 064 textTrackCommentPane.setEditable(false); 065 textTrackCommentPane.setMaximumSize(new Dimension(1000, 200)); 066 067 JPanel pTrackSelect = new JPanel(); 068 pTrackSelect.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Track"))); 069 pTrackSelect.add(trackComboBox); 070 // add next button for web server 071 pTrackSelect.add(nextButton); 072 073 // work at this location by track 074 pTrack.setLayout(new BoxLayout(pTrack, BoxLayout.Y_AXIS)); 075 pTrackPane = new JScrollPane(pTrack); 076 077 pLocationName.setMaximumSize(new Dimension(2000, 200)); 078 pTrackSelect.setMaximumSize(new Dimension(2000, 200)); 079 pButtons.setMaximumSize(new Dimension(2000, 200)); 080 081 add(pLocationName); 082 add(textLocationCommentPane); 083 add(textSwitchListCommentPane); 084 add(pTrackSelect); 085 add(textTrackCommentPane); 086 add(pTrackComments); 087 add(pTrackPane); 088 add(pButtons); 089 090 if (_location != null) { 091 textLocationName.setText(_location.getName()); 092 loadLocationComment(_location); 093 loadLocationSwitchListComment(_location); 094 updateTrackComboBox(); 095 _location.addPropertyChangeListener(this); 096 } 097 098 update(); 099 100 addComboBoxAction(trackComboBox); 101 addButtonAction(nextButton); 102 } 103 104 // Select, Clear, and Next Buttons 105 @Override 106 public void buttonActionPerformed(ActionEvent ae) { 107 if (ae.getSource() == nextButton) { 108 nextButtonAction(); 109 } 110 super.buttonActionPerformed(ae); 111 } 112 113 private void nextButtonAction() { 114 log.debug("next button activated"); 115 if (trackComboBox.getItemCount() > 1) { 116 int index = trackComboBox.getSelectedIndex(); 117 // index = -1 if first item (null) in trainComboBox 118 if (index == -1) { 119 index = 1; 120 } else { 121 index++; 122 } 123 if (index >= trackComboBox.getItemCount()) { 124 index = 0; 125 } 126 trackComboBox.setSelectedIndex(index); 127 } 128 } 129 130 @Override 131 protected void comboBoxActionPerformed(ActionEvent ae) { 132 // made the combo box not visible during updates, so ignore if not visible 133 if (ae.getSource() == trackComboBox && trackComboBox.isVisible()) { 134 _track = null; 135 if (trackComboBox.getSelectedItem() != null) { 136 _track = (Track) trackComboBox.getSelectedItem(); 137 } 138 update(); 139 } 140 } 141 142 @Override 143 protected void update() { 144 // use invokeLater to prevent deadlock 145 SwingUtilities.invokeLater(() -> { 146 runUpdate(); 147 }); 148 } 149 150 private void runUpdate() { 151 log.debug("run update"); 152 removePropertyChangeListerners(); 153 trainCommon.clearUtilityCarTypes(); // reset the utility car counts 154 checkBoxes.clear(); 155 pTrack.removeAll(); 156 if (_track != null) { 157 pTrackPane.setBorder(BorderFactory.createTitledBorder(_track.getName())); 158 textTrackCommentPane.setText(TrainCommon.getTextColorString(_track.getComment())); 159 textTrackCommentPane.setForeground(TrainCommon.getTextColor(_track.getComment())); 160 textTrackCommentPane.setVisible(!_track.getComment().equals(Track.NONE)); 161 for (Train train : trainManager.getTrainsArrivingThisLocationList(_track.getLocation())) { 162 JPanel pTrain = new JPanel(); 163 pTrain.setLayout(new BoxLayout(pTrain, BoxLayout.Y_AXIS)); 164 pTrain.setBorder(BorderFactory 165 .createTitledBorder(MessageFormat.format(TrainSwitchListText.getStringScheduledWork(), 166 new Object[] { train.getName(), train.getDescription() }))); 167 // Track work comments 168 boolean pickupCar = false; 169 boolean setoutCar = false; 170 JTextPane textTrackCommentWorkPane = getTrackWorkCommentPane(); 171 pTrain.add(textTrackCommentWorkPane); 172 173 boolean localCar = false; 174 175 // Engine 176 boolean pickupEngine = false; 177 boolean setoutEngine = false; 178 179 // pick ups 180 JPanel pPickups = new JPanel(); 181 pPickups.setLayout(new BoxLayout(pPickups, BoxLayout.Y_AXIS)); 182 pPickups.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Pickup"))); 183 pPickups.setMaximumSize(new Dimension(2000, 2000)); 184 185 // set outs 186 JPanel pSetouts = new JPanel(); 187 pSetouts.setLayout(new BoxLayout(pSetouts, BoxLayout.Y_AXIS)); 188 pSetouts.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SetOut"))); 189 pSetouts.setMaximumSize(new Dimension(2000, 2000)); 190 191 // local moves 192 JPanel pLocal = new JPanel(); 193 pLocal.setLayout(new BoxLayout(pLocal, BoxLayout.Y_AXIS)); 194 pLocal.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocalMoves"))); 195 pLocal.setMaximumSize(new Dimension(2000, 2000)); 196 197 // List locos first 198 List<Engine> engList = engManager.getByTrainBlockingList(train); 199 if (Setup.isPrintHeadersEnabled()) { 200 for (Engine engine : engList) { 201 if (engine.getTrack() == _track) { 202 JLabel header = new JLabel(Tab + trainCommon.getPickupEngineHeader()); 203 setLabelFont(header); 204 pPickups.add(header); 205 break; 206 } 207 } 208 } 209 for (Engine engine : engList) { 210 if (engine.getTrack() == _track) { 211 engine.addPropertyChangeListener(this); 212 rollingStock.add(engine); 213 JCheckBox checkBox = new JCheckBox(trainCommon.pickupEngine(engine)); 214 setCheckBoxFont(checkBox, Setup.getPickupColor()); 215 pPickups.add(checkBox); 216 pickupEngine = true; 217 checkBoxes.put(engine.getId() + "p", checkBox); 218 pTrack.add(pTrain); 219 } 220 } 221 // now do locomotive set outs 222 if (Setup.isPrintHeadersEnabled()) { 223 for (Engine engine : engList) { 224 if (engine.getDestinationTrack() == _track) { 225 JLabel header = new JLabel(Tab + trainCommon.getDropEngineHeader()); 226 setLabelFont(header); 227 pSetouts.add(header); 228 break; 229 } 230 } 231 } 232 for (Engine engine : engList) { 233 if (engine.getDestinationTrack() == _track) { 234 engine.addPropertyChangeListener(this); 235 rollingStock.add(engine); 236 JCheckBox checkBox = new JCheckBox(trainCommon.dropEngine(engine)); 237 setCheckBoxFont(checkBox, Setup.getDropColor()); 238 pSetouts.add(checkBox); 239 setoutEngine = true; 240 checkBoxes.put(engine.getId(), checkBox); 241 pTrack.add(pTrain); 242 } 243 } 244 // now cars 245 List<Car> carList = carManager.getByTrainDestinationList(train); 246 if (Setup.isPrintHeadersEnabled()) { 247 for (Car car : carList) { 248 if (car.getTrack() == _track && car.getRouteDestination() != car.getRouteLocation()) { 249 JLabel header = new JLabel(Tab + 250 trainCommon.getPickupCarHeader(!IS_MANIFEST, !TrainCommon.IS_TWO_COLUMN_TRACK)); 251 setLabelFont(header); 252 pPickups.add(header); 253 break; 254 } 255 } 256 } 257 // sort car pick ups by their destination 258 List<RouteLocation> routeList = train.getRoute().getLocationsBySequenceList(); 259 for (RouteLocation rl : routeList) { 260 for (Car car : carList) { 261 if (car.getTrack() == _track && 262 car.getRouteDestination() != car.getRouteLocation() && 263 car.getRouteDestination() == rl) { 264 car.addPropertyChangeListener(this); 265 rollingStock.add(car); 266 String text; 267 if (car.isUtility()) { 268 text = trainCommon.pickupUtilityCars(carList, car, !IS_MANIFEST, 269 !TrainCommon.IS_TWO_COLUMN_TRACK); 270 if (text == null) { 271 continue; // this car type has already been processed 272 } 273 } else { 274 text = trainCommon.pickupCar(car, !IS_MANIFEST, !TrainCommon.IS_TWO_COLUMN_TRACK); 275 } 276 pickupCar = true; 277 JCheckBox checkBox = new JCheckBox(text); 278 setCheckBoxFont(checkBox, Setup.getPickupColor()); 279 pPickups.add(checkBox); 280 checkBoxes.put(car.getId() + "p", checkBox); 281 pTrack.add(pTrain); 282 } 283 } 284 } 285 // now do car set outs 286 if (Setup.isPrintHeadersEnabled()) { 287 for (Car car : carList) { 288 if (car.getDestinationTrack() == _track && 289 car.getRouteDestination() != car.getRouteLocation()) { 290 JLabel header = new JLabel( 291 Tab + trainCommon.getDropCarHeader(!IS_MANIFEST, !TrainCommon.IS_TWO_COLUMN_TRACK)); 292 setLabelFont(header); 293 pSetouts.add(header); 294 break; 295 } 296 } 297 } 298 for (Car car : carList) { 299 if (car.getDestinationTrack() == _track && car.getRouteLocation() != car.getRouteDestination()) { 300 car.addPropertyChangeListener(this); 301 rollingStock.add(car); 302 String text; 303 if (car.isUtility()) { 304 text = trainCommon.setoutUtilityCars(carList, car, !TrainCommon.LOCAL, !IS_MANIFEST); 305 if (text == null) { 306 continue; // this car type has already been processed 307 } 308 } else { 309 text = trainCommon.dropCar(car, !IS_MANIFEST, !TrainCommon.IS_TWO_COLUMN_TRACK); 310 } 311 setoutCar = true; 312 JCheckBox checkBox = new JCheckBox(text); 313 setCheckBoxFont(checkBox, Setup.getDropColor()); 314 pSetouts.add(checkBox); 315 checkBoxes.put(car.getId(), checkBox); 316 pTrack.add(pTrain); 317 } 318 } 319 // now do local car moves 320 if (Setup.isPrintHeadersEnabled()) { 321 for (Car car : carList) { 322 if ((car.getTrack() == _track || car.getDestinationTrack() == _track) && 323 car.getRouteDestination() == car.getRouteLocation()) { 324 JLabel header = new JLabel(Tab + trainCommon.getLocalMoveHeader(!IS_MANIFEST)); 325 setLabelFont(header); 326 pLocal.add(header); 327 break; 328 } 329 } 330 } 331 for (Car car : carList) { 332 if ((car.getTrack() == _track || car.getDestinationTrack() == _track) && 333 car.getRouteLocation() != null && 334 car.getRouteLocation() == car.getRouteDestination()) { 335 car.addPropertyChangeListener(this); 336 rollingStock.add(car); 337 String text; 338 if (car.isUtility()) { 339 text = trainCommon.setoutUtilityCars(carList, car, TrainCommon.LOCAL, !IS_MANIFEST); 340 if (text == null) { 341 continue; // this car type has already been processed 342 } 343 } else { 344 text = trainCommon.localMoveCar(car, !IS_MANIFEST); 345 } 346 if (car.getTrack() == _track) { 347 pickupCar = true; 348 } 349 if (car.getDestinationTrack() == _track) { 350 setoutCar = true; 351 } 352 JCheckBox checkBox = new JCheckBox(text); 353 setCheckBoxFont(checkBox, Setup.getLocalColor()); 354 pLocal.add(checkBox); 355 localCar = true; 356 checkBoxes.put(car.getId(), checkBox); 357 pTrack.add(pTrain); 358 } 359 } 360 if (pickupCar && !setoutCar) { 361 textTrackCommentWorkPane.setText(_track.getCommentPickup()); 362 textTrackCommentWorkPane 363 .setForeground(TrainCommon.getTextColor(_track.getCommentPickupWithColor())); 364 } else if (!pickupCar && setoutCar) { 365 textTrackCommentWorkPane.setText(_track.getCommentSetout()); 366 textTrackCommentWorkPane 367 .setForeground(TrainCommon.getTextColor(_track.getCommentSetoutWithColor())); 368 } else if (pickupCar && setoutCar) { 369 textTrackCommentWorkPane.setText(_track.getCommentBoth()); 370 textTrackCommentWorkPane.setForeground(TrainCommon.getTextColor(_track.getCommentBothWithColor())); 371 } 372 textTrackCommentWorkPane.setVisible(!textTrackCommentWorkPane.getText().isEmpty()); 373 374 // only show panels that have work 375 if (pickupCar || pickupEngine) { 376 pTrain.add(pPickups); 377 } 378 if (setoutCar || setoutEngine) { 379 pTrain.add(pSetouts); 380 } 381 if (localCar) { 382 pTrain.add(pLocal); 383 } 384 385 pTrackPane.validate(); 386 pTrain.setMaximumSize(new Dimension(2000, pTrain.getHeight())); 387 pTrain.revalidate(); 388 } 389 // now do car holds 390 // we only need the cars on this track 391 List<Car> rsList = carManager.getByTrainList(); 392 List<Car> carList = new ArrayList<Car>(); 393 for (Car rs : rsList) { 394 if (rs.getTrack() != _track || rs.getRouteLocation() != null) 395 continue; 396 carList.add(rs); 397 } 398 JPanel pHoldCars = new JPanel(); 399 pHoldCars.setLayout(new BoxLayout(pHoldCars, BoxLayout.Y_AXIS)); 400 pHoldCars.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("HoldCars"))); 401 for (Car car : carList) { 402 String text; 403 if (car.isUtility()) { 404 String s = trainCommon.pickupUtilityCars(carList, car, !IS_MANIFEST, 405 !TrainCommon.IS_TWO_COLUMN_TRACK); 406 if (s == null) 407 continue; 408 text = TrainSwitchListText.getStringHoldCar().split("\\{")[0] + s.trim(); 409 } else { 410 text = MessageFormat.format(TrainSwitchListText.getStringHoldCar(), 411 new Object[] { 412 TrainCommon.padAndTruncateIfNeeded(car.getRoadName(), 413 InstanceManager.getDefault(CarRoads.class).getMaxNameLength()), 414 TrainCommon.padAndTruncateIfNeeded(TrainCommon.splitString(car.getNumber()), 415 Control.max_len_string_print_road_number), 416 TrainCommon.padAndTruncateIfNeeded(car.getTypeName().split(TrainCommon.HYPHEN)[0], 417 InstanceManager.getDefault(CarTypes.class).getMaxNameLength()), 418 TrainCommon.padAndTruncateIfNeeded(car.getLength() + Setup.getLengthUnitAbv(), 419 Control.max_len_string_length_name), 420 TrainCommon.padAndTruncateIfNeeded(car.getLoadName(), 421 InstanceManager.getDefault(CarLoads.class).getMaxNameLength()), 422 TrainCommon.padAndTruncateIfNeeded(_track.getName(), 423 InstanceManager.getDefault(LocationManager.class).getMaxTrackNameLength()), 424 TrainCommon.padAndTruncateIfNeeded(car.getColor(), 425 InstanceManager.getDefault(CarColors.class).getMaxNameLength()) }); 426 427 } 428 JCheckBox checkBox = new JCheckBox(text); 429 setCheckBoxFont(checkBox, Color.black); 430 pHoldCars.add(checkBox); 431 checkBoxes.put(car.getId(), checkBox); 432 pTrack.add(pHoldCars); 433 } 434 pTrackPane.validate(); 435 pHoldCars.setMaximumSize(new Dimension(2000, pHoldCars.getHeight())); 436 pHoldCars.revalidate(); 437 } else { 438 pTrackPane.setBorder(BorderFactory.createTitledBorder("")); 439 textTrackCommentPane.setVisible(false); 440 } 441 } 442 443 private JTextPane getTrackWorkCommentPane() { 444 JTextPane textTrackCommentWorkPane = new JTextPane(); 445 textTrackCommentWorkPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment"))); 446 textTrackCommentWorkPane.setBackground(null); 447 textTrackCommentWorkPane.setEditable(false); 448 return textTrackCommentWorkPane; 449 } 450 451 private void updateTrackComboBox() { 452 Object selectedItem = trackComboBox.getSelectedItem(); 453 trackComboBox.setVisible(false); // used as a flag to ignore updates 454 if (_location != null) { 455 _location.updateComboBox(trackComboBox); 456 } 457 if (selectedItem != null) { 458 trackComboBox.setSelectedItem(selectedItem); 459 } 460 trackComboBox.setVisible(true); 461 } 462 463 @Override 464 public void dispose() { 465 if (_location != null) 466 _location.removePropertyChangeListener(this); 467 removePropertyChangeListerners(); 468 } 469 470 @Override 471 public void propertyChange(java.beans.PropertyChangeEvent e) { 472 if (Control.SHOW_PROPERTY) { 473 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), 474 e.getNewValue()); 475 } 476 if (e.getPropertyName().equals(RollingStock.ROUTE_LOCATION_CHANGED_PROPERTY)) { 477 update(); 478 } 479 if (e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY)) { 480 updateTrackComboBox(); 481 } 482 } 483 484 private final static Logger log = LoggerFactory.getLogger(YardmasterByTrackPanel.class); 485}