001package jmri.jmrit.operations; 002 003import java.awt.*; 004import java.awt.event.ActionEvent; 005import java.beans.PropertyChangeEvent; 006import java.beans.PropertyChangeListener; 007import java.text.MessageFormat; 008import java.util.*; 009import java.util.List; 010import java.util.concurrent.ConcurrentHashMap; 011 012import javax.swing.*; 013 014import jmri.InstanceManager; 015import jmri.jmrit.operations.locations.Location; 016import jmri.jmrit.operations.locations.Track; 017import jmri.jmrit.operations.rollingstock.RollingStock; 018import jmri.jmrit.operations.rollingstock.cars.*; 019import jmri.jmrit.operations.rollingstock.cars.gui.CarSetFrame; 020import jmri.jmrit.operations.rollingstock.cars.gui.CarsTableFrame; 021import jmri.jmrit.operations.rollingstock.engines.*; 022import jmri.jmrit.operations.rollingstock.engines.gui.EngineSetFrame; 023import jmri.jmrit.operations.routes.Route; 024import jmri.jmrit.operations.routes.RouteLocation; 025import jmri.jmrit.operations.setup.Control; 026import jmri.jmrit.operations.setup.Setup; 027import jmri.jmrit.operations.trains.*; 028import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 029import jmri.util.swing.JmriJOptionPane; 030 031/** 032 * Common elements for the Conductor and Yardmaster Frames. 033 * 034 * @author Dan Boudreau Copyright (C) 2013 035 * 036 */ 037public abstract class CommonConductorYardmasterPanel extends OperationsPanel implements PropertyChangeListener { 038 039 protected static final boolean IS_MANIFEST = true; 040 041 protected static final String Tab = " "; // used to space out headers 042 protected static final String Space = " "; // used to pad out panels 043 044 protected Location _location = null; 045 protected Train _train = null; 046 047 protected TrainManager trainManager = InstanceManager.getDefault(TrainManager.class); 048 protected EngineManager engManager = InstanceManager.getDefault(EngineManager.class); 049 protected CarManager carManager = InstanceManager.getDefault(CarManager.class); 050 protected TrainCommon trainCommon = new TrainCommon(); 051 052 protected JScrollPane locoPane; 053 protected JScrollPane pickupPane; 054 protected JScrollPane setoutPane; 055 protected JScrollPane movePane; 056 057 // labels 058 protected JLabel textRailRoadName = new JLabel(); 059 protected JLabel textTrainDescription = new JLabel(); 060 protected JLabel textLocationName = new JLabel(); 061 protected JLabel textStatus = new JLabel(); 062 063 // major buttons 064 public JButton selectButton = new JButton(Bundle.getMessage("SelectAll")); 065 public JButton clearButton = new JButton(Bundle.getMessage("ClearAll")); 066 public JButton modifyButton = new JButton(Bundle.getMessage("Modify")); // see setModifyButtonText() 067 public JButton moveButton = new JButton(Bundle.getMessage("Move")); 068 069 // text panes 070 protected JTextPane textLocationCommentPane = new JTextPane(); 071 protected JTextPane textTrainCommentPane = new JTextPane(); 072 protected JTextPane textTrainRouteCommentPane = new JTextPane(); 073 protected JTextPane textTrainRouteLocationCommentPane = new JTextPane(); 074 protected JTextPane textSwitchListCommentPane = new JTextPane(); 075 protected JTextPane textTrainStatusPane = new JTextPane(); 076 077 // panels 078 protected JPanel pRailRoadName = new JPanel(); 079 080 protected JPanel pTrainDescription = new JPanel(); 081 082 protected JPanel pLocationName = new JPanel(); 083 084 protected JPanel pTrackComments = new JPanel(); 085 086 protected JPanel pLocos = new JPanel(); 087 protected JPanel pPickupLocos = new JPanel(); 088 protected JPanel pSetoutLocos = new JPanel(); 089 090 protected JPanel pPickups = new JPanel(); 091 protected JPanel pSetouts = new JPanel(); 092 protected JPanel pWorkPanes = new JPanel(); // place car pick ups and set outs side by side using two columns 093 protected JPanel pMoves = new JPanel(); 094 095 protected JPanel pStatus = new JPanel(); 096 protected JPanel pButtons = new JPanel(); 097 098 // check boxes 099 protected ConcurrentHashMap<String, JCheckBox> checkBoxes = new ConcurrentHashMap<>(); 100 protected List<RollingStock> rollingStock = Collections.synchronizedList(new ArrayList<>()); 101 102 // flags 103 protected boolean isSetMode = false; // when true, cars that aren't selected (checkbox) can be "set" 104 105 public CommonConductorYardmasterPanel() { 106 super(); 107 initComponents(); 108 } 109 110 public void initComponents() { 111 112 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 113 114 locoPane = new JScrollPane(pLocos); 115 locoPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Engines"))); 116 117 pickupPane = new JScrollPane(pPickups); 118 pickupPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Pickup"))); 119 120 setoutPane = new JScrollPane(pSetouts); 121 setoutPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SetOut"))); 122 123 movePane = new JScrollPane(pMoves); 124 movePane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocalMoves"))); 125 126 // Set up the panels 127 pTrackComments.setLayout(new BoxLayout(pTrackComments, BoxLayout.Y_AXIS)); 128 pPickupLocos.setLayout(new BoxLayout(pPickupLocos, BoxLayout.Y_AXIS)); 129 pSetoutLocos.setLayout(new BoxLayout(pSetoutLocos, BoxLayout.Y_AXIS)); 130 pPickups.setLayout(new BoxLayout(pPickups, BoxLayout.Y_AXIS)); 131 pSetouts.setLayout(new BoxLayout(pSetouts, BoxLayout.Y_AXIS)); 132 pMoves.setLayout(new BoxLayout(pMoves, BoxLayout.Y_AXIS)); 133 134 // railroad name 135 pRailRoadName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RailroadName"))); 136 pRailRoadName.add(textRailRoadName); 137 138 // location name 139 pLocationName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location"))); 140 pLocationName.add(textLocationName); 141 142 // location comment 143 textLocationCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocationComment"))); 144 textLocationCommentPane.setBackground(null); 145 textLocationCommentPane.setEditable(false); 146 textLocationCommentPane.setMaximumSize(new Dimension(2000, 200)); 147 148 // train description 149 pTrainDescription.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Description"))); 150 pTrainDescription.add(textTrainDescription); 151 152 // train comment 153 textTrainCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainComment"))); 154 textTrainCommentPane.setBackground(null); 155 textTrainCommentPane.setEditable(false); 156 textTrainCommentPane.setMaximumSize(new Dimension(2000, 200)); 157 158 // train route comment 159 textTrainRouteCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RouteComment"))); 160 textTrainRouteCommentPane.setBackground(null); 161 textTrainRouteCommentPane.setEditable(false); 162 textTrainRouteCommentPane.setMaximumSize(new Dimension(2000, 200)); 163 164 // train route location comment 165 textTrainRouteLocationCommentPane 166 .setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RouteLocationComment"))); 167 textTrainRouteLocationCommentPane.setBackground(null); 168 textTrainRouteLocationCommentPane.setEditable(false); 169 textTrainRouteLocationCommentPane.setMaximumSize(new Dimension(2000, 200)); 170 171 // Switch list location comment 172 textSwitchListCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment"))); 173 textSwitchListCommentPane.setBackground(null); 174 textSwitchListCommentPane.setEditable(false); 175 textSwitchListCommentPane.setMaximumSize(new Dimension(2000, 200)); 176 177 // Train status 178 textTrainStatusPane.setBorder(BorderFactory.createTitledBorder("")); 179 textTrainStatusPane.setBackground(null); 180 textTrainStatusPane.setEditable(false); 181 textTrainStatusPane.setMaximumSize(new Dimension(2000, 200)); 182 183 // row 12 184 pLocos.setLayout(new BoxLayout(pLocos, BoxLayout.Y_AXIS)); 185 pWorkPanes.setLayout(new BoxLayout(pWorkPanes, BoxLayout.Y_AXIS)); 186 187 pLocos.add(pPickupLocos); 188 pLocos.add(pSetoutLocos); 189 pWorkPanes.add(pickupPane); 190 pWorkPanes.add(setoutPane); 191 192 // row 13 193 pStatus.setLayout(new GridBagLayout()); 194 pStatus.setBorder(BorderFactory.createTitledBorder("")); 195 addItem(pStatus, textStatus, 0, 0); 196 197 // row 14 198 pButtons.setLayout(new GridBagLayout()); 199 pButtons.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Work"))); 200 addItem(pButtons, selectButton, 0, 0); 201 addItem(pButtons, clearButton, 1, 0); 202 addItem(pButtons, modifyButton, 2, 0); 203 204 // setup buttons 205 addButtonAction(selectButton); 206 addButtonAction(clearButton); 207 addButtonAction(modifyButton); 208 } 209 210 // Select, Clear, and Set Buttons 211 @Override 212 public void buttonActionPerformed(ActionEvent ae) { 213 if (ae.getSource() == selectButton) { 214 selectCheckboxes(true); 215 } 216 if (ae.getSource() == clearButton) { 217 selectCheckboxes(false); 218 } 219 if (ae.getSource() == modifyButton) { 220 isSetMode = !isSetMode; // toggle setMode 221 update(); 222 // ask if user wants to add cars to train 223 if (isSetMode) { 224 addCarToTrain(); 225 } 226 } 227 check(); 228 } 229 230 protected void initialize() { 231 removePropertyChangeListerners(); 232 pTrackComments.removeAll(); 233 pPickupLocos.removeAll(); 234 pSetoutLocos.removeAll(); 235 pPickups.removeAll(); 236 pSetouts.removeAll(); 237 pMoves.removeAll(); 238 239 // turn everything off and re-enable if needed 240 pWorkPanes.setVisible(false); 241 pickupPane.setVisible(false); 242 setoutPane.setVisible(false); 243 locoPane.setVisible(false); 244 pPickupLocos.setVisible(false); 245 pSetoutLocos.setVisible(false); 246 movePane.setVisible(false); 247 248 textTrainRouteLocationCommentPane.setVisible(false); 249 250 setModifyButtonText(); 251 } 252 253 protected void updateComplete() { 254 pTrackComments.repaint(); 255 pPickupLocos.repaint(); 256 pSetoutLocos.repaint(); 257 pPickups.repaint(); 258 pSetouts.repaint(); 259 pMoves.repaint(); 260 261 pTrackComments.revalidate(); 262 pPickupLocos.revalidate(); 263 pSetoutLocos.revalidate(); 264 pPickups.revalidate(); 265 pSetouts.revalidate(); 266 pMoves.revalidate(); 267 268 selectButton.setEnabled(!checkBoxes.isEmpty() && !isSetMode); 269 clearButton.setEnabled(!checkBoxes.isEmpty() && !isSetMode); 270 check(); 271 272 log.debug("update complete"); 273 } 274 275 private void addCarToTrain() { 276 if (JmriJOptionPane.showConfirmDialog(this, 277 Bundle.getMessage("WantAddCarsToTrain?", _train.getName()), 278 Bundle.getMessage("AddCarsToTrain?"), JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) { 279 new CarsTableFrame(false, _train.getCurrentRouteLocation().getName(), null); 280 } 281 } 282 283 CarSetFrame csf = null; 284 285 // action for set button for a car, opens the set car window 286 public void carSetButtonActionPerfomed(ActionEvent ae) { 287 String name = ((JButton) ae.getSource()).getName(); 288 log.debug("Set button for car {}", name); 289 Car car = carManager.getById(name); 290 if (csf != null) { 291 csf.dispose(); 292 } 293 csf = new CarSetFrame(); 294 csf.initComponents(); 295 csf.load(car); 296 } 297 298 EngineSetFrame esf = null; 299 300 // action for set button for an engine, opens the set engine window 301 public void engineSetButtonActionPerfomed(ActionEvent ae) { 302 String name = ((JButton) ae.getSource()).getName(); 303 log.debug("Set button for loco {}", name); 304 Engine eng = engManager.getById(name); 305 if (esf != null) { 306 esf.dispose(); 307 } 308 esf = new EngineSetFrame(); 309 esf.initComponents(); 310 esf.load(eng); 311 } 312 313 // confirm that all work is done 314 @Override 315 protected void checkBoxActionPerformed(ActionEvent ae) { 316 check(); 317 } 318 319 // Determines if all car checkboxes are selected. Disables the Set button if 320 // all checkbox are selected. 321 protected void check() { 322 Enumeration<JCheckBox> en = checkBoxes.elements(); 323 while (en.hasMoreElements()) { 324 JCheckBox checkBox = en.nextElement(); 325 if (!checkBox.isSelected()) { 326 // log.debug("Checkbox (" + checkBox.getText() + ") isn't selected "); 327 moveButton.setEnabled(false); 328 modifyButton.setEnabled(true); 329 return; 330 } 331 } 332 // all selected, work done! 333 moveButton.setEnabled(_train != null && _train.isBuilt()); 334 modifyButton.setEnabled(false); 335 isSetMode = false; 336 setModifyButtonText(); 337 } 338 339 protected void selectCheckboxes(boolean enable) { 340 Enumeration<JCheckBox> en = checkBoxes.elements(); 341 while (en.hasMoreElements()) { 342 JCheckBox checkBox = en.nextElement(); 343 checkBox.setSelected(enable); 344 } 345 isSetMode = false; 346 } 347 348 protected void loadTrainDescription() { 349 textTrainDescription.setText(TrainCommon.getTextColorString(_train.getDescription())); 350 textTrainDescription.setForeground(TrainCommon.getTextColor(_train.getDescription())); 351 } 352 353 /** 354 * show train comment box only if there's a comment 355 */ 356 protected void loadTrainComment() { 357 if (_train.getComment().equals(Train.NONE)) { 358 textTrainCommentPane.setVisible(false); 359 } else { 360 textTrainCommentPane.setVisible(true); 361 textTrainCommentPane.setText(_train.getComment()); 362 textTrainCommentPane.setForeground(TrainCommon.getTextColor(_train.getCommentWithColor())); 363 } 364 } 365 366 protected void loadRailroadName() { 367 // Does this train have a unique railroad name? 368 if (!_train.getRailroadName().equals(Train.NONE)) { 369 textRailRoadName.setText(TrainCommon.getTextColorString(_train.getRailroadName())); 370 textRailRoadName.setForeground(TrainCommon.getTextColor(_train.getRailroadName())); 371 } else { 372 textRailRoadName.setText(Setup.getRailroadName()); 373 } 374 } 375 376 protected void loadLocationComment(Location location) { 377 textLocationCommentPane 378 .setVisible(!location.getComment().isEmpty() && Setup.isPrintLocationCommentsEnabled()); 379 if (textLocationCommentPane.isVisible()) { 380 textLocationCommentPane.setText(location.getComment()); 381 textLocationCommentPane.setForeground(TrainCommon.getTextColor(location.getCommentWithColor())); 382 } 383 } 384 385 protected void loadLocationSwitchListComment(Location location) { 386 textSwitchListCommentPane.setVisible(!location.getSwitchListComment().isEmpty()); 387 if (textSwitchListCommentPane.isVisible()) { 388 textSwitchListCommentPane.setText(location.getSwitchListComment()); 389 textSwitchListCommentPane.setForeground(TrainCommon.getTextColor(location.getSwitchListCommentWithColor())); 390 } 391 } 392 393 /** 394 * show route comment box only if there's a route comment 395 */ 396 protected void loadRouteComment() { 397 if (_train.getRoute() != null && _train.getRoute().getComment().equals(Route.NONE) || 398 !Setup.isPrintRouteCommentsEnabled()) { 399 textTrainRouteCommentPane.setVisible(false); 400 } else { 401 textTrainRouteCommentPane.setVisible(true); 402 textTrainRouteCommentPane.setText(TrainCommon.getTextColorString(_train.getRoute().getComment())); 403 textTrainRouteCommentPane.setForeground(TrainCommon.getTextColor(_train.getRoute().getComment())); 404 } 405 } 406 407 protected void loadRouteLocationComment(RouteLocation rl) { 408 textTrainRouteLocationCommentPane.setVisible(!rl.getComment().equals(RouteLocation.NONE)); 409 if (textTrainRouteLocationCommentPane.isVisible()) { 410 textTrainRouteLocationCommentPane.setText(rl.getComment()); 411 textTrainRouteLocationCommentPane.setForeground(rl.getCommentColor()); 412 } 413 } 414 415 protected void updateTrackComments(RouteLocation rl, boolean isManifest) { 416 Location location = rl.getLocation(); 417 if (location != null) { 418 List<Track> tracks = location.getTracksByNameList(null); 419 for (Track track : tracks) { 420 if (isManifest && !track.isPrintManifestCommentEnabled() || 421 !isManifest && !track.isPrintSwitchListCommentEnabled()) { 422 continue; 423 } 424 // any pick ups or set outs to this track? 425 boolean pickup = false; 426 boolean setout = false; 427 List<Car> carList = carManager.getByTrainDestinationList(_train); 428 for (Car car : carList) { 429 if (car.getRouteLocation() == rl && car.getTrack() != null && car.getTrack() == track) { 430 pickup = true; 431 } 432 if (car.getRouteDestination() == rl && 433 car.getDestinationTrack() != null && 434 car.getDestinationTrack() == track) { 435 setout = true; 436 } 437 } 438 // display the appropriate comment if there's one 439 if (pickup || setout) { 440 JTextPane commentTextPane = new JTextPane(); 441 if (pickup && setout && !track.getCommentBoth().equals(Track.NONE)) { 442 commentTextPane.setText(track.getCommentBoth()); 443 commentTextPane.setForeground(TrainCommon.getTextColor(track.getCommentBothWithColor())); 444 } else if (pickup && !setout && !track.getCommentPickup().equals(Track.NONE)) { 445 commentTextPane.setText(track.getCommentPickup()); 446 commentTextPane.setForeground(TrainCommon.getTextColor(track.getCommentPickupWithColor())); 447 } else if (!pickup && setout && !track.getCommentSetout().equals(Track.NONE)) { 448 commentTextPane.setText(track.getCommentSetout()); 449 commentTextPane.setForeground(TrainCommon.getTextColor(track.getCommentSetoutWithColor())); 450 } 451 if (!commentTextPane.getText().isEmpty()) { 452 commentTextPane.setBorder( 453 BorderFactory.createTitledBorder(Bundle.getMessage("Comment") + " " + track.getName())); 454 commentTextPane.setBackground(null); 455 commentTextPane.setEditable(false); 456 commentTextPane.setMaximumSize(new Dimension(2000, 200)); 457 pTrackComments.add(commentTextPane); 458 pTrackComments.setVisible(true); 459 } 460 } 461 } 462 } 463 } 464 465 /** 466 * Uses "ep" prefix to denote a checkbox with an engine pick up, and "es" for an 467 * engine set out. 468 * 469 * @param rl The routeLocation to show loco pick ups or set outs. 470 */ 471 protected void updateLocoPanes(RouteLocation rl) { 472 if (Setup.isPrintHeadersEnabled()) { 473 JLabel header = new JLabel(Tab + trainCommon.getPickupEngineHeader()); 474 setLabelFont(header); 475 pPickupLocos.add(header); 476 JLabel headerDrop = new JLabel(Tab + trainCommon.getDropEngineHeader()); 477 setLabelFont(headerDrop); 478 pSetoutLocos.add(headerDrop); 479 } 480 // check for locos 481 List<Engine> engList = engManager.getByTrainBlockingList(_train); 482 for (Engine engine : engList) { 483 if (engine.getRouteLocation() == rl && engine.getTrack() != null) { 484 locoPane.setVisible(true); 485 pPickupLocos.setVisible(true); 486 rollingStock.add(engine); 487 engine.addPropertyChangeListener(this); 488 JCheckBox checkBox; 489 if (checkBoxes.containsKey("ep" + engine.getId())) { 490 checkBox = checkBoxes.get("ep" + engine.getId()); 491 } else { 492 checkBox = new JCheckBox(trainCommon.pickupEngine(engine)); 493 setCheckBoxFont(checkBox, Setup.getPickupEngineColor()); 494 addCheckBoxAction(checkBox); 495 checkBoxes.put("ep" + engine.getId(), checkBox); 496 } 497 if (isSetMode && !checkBox.isSelected()) { 498 pPickupLocos.add(addSet(engine)); 499 } else { 500 pPickupLocos.add(checkBox); 501 } 502 } 503 if (engine.getRouteDestination() == rl) { 504 locoPane.setVisible(true); 505 pSetoutLocos.setVisible(true); 506 rollingStock.add(engine); 507 engine.addPropertyChangeListener(this); 508 JCheckBox checkBox; 509 if (checkBoxes.containsKey("es" + engine.getId())) { 510 checkBox = checkBoxes.get("es" + engine.getId()); 511 } else { 512 checkBox = new JCheckBox(trainCommon.dropEngine(engine)); 513 setCheckBoxFont(checkBox, Setup.getDropEngineColor()); 514 addCheckBoxAction(checkBox); 515 checkBoxes.put("es" + engine.getId(), checkBox); 516 } 517 if (isSetMode && !checkBox.isSelected()) { 518 pSetoutLocos.add(addSet(engine)); 519 } else { 520 pSetoutLocos.add(checkBox); 521 } 522 } 523 } 524 // pad the panels in case the horizontal scroll bar appears 525 pPickupLocos.add(new JLabel(Space)); 526 pSetoutLocos.add(new JLabel(Space)); 527 } 528 529 /** 530 * Block cars by track (optional), then pick up and set out for each location in 531 * a train's route. This shows each car with a check box or with a set button. 532 * The set button is displayed when the checkbox isn't selected and the display 533 * is in "set" mode. If the car is a utility. Show the number of cars that have 534 * the same attributes, and not the car's road and number. Each car is displayed 535 * only once in one of three panes. The three panes are pick up, set out, or 536 * local move. To keep track of each car and which pane to use, they are placed 537 * in the list "rollingStock" with the prefix "p", "s" or "m" and the car's 538 * unique id. 539 * 540 * @param rl The RouteLocation 541 * @param isManifest True if manifest, false if switch list 542 * 543 */ 544 protected void blockCars(RouteLocation rl, boolean isManifest) { 545 if (Setup.isPrintHeadersEnabled()) { 546 JLabel header = new JLabel( 547 Tab + trainCommon.getPickupCarHeader(isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK)); 548 setLabelFont(header); 549 pPickups.add(header); 550 header = new JLabel(Tab + trainCommon.getDropCarHeader(isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK)); 551 setLabelFont(header); 552 pSetouts.add(header); 553 header = new JLabel(Tab + trainCommon.getLocalMoveHeader(isManifest)); 554 setLabelFont(header); 555 pMoves.add(header); 556 } 557 List<Track> tracks = rl.getLocation().getTracksByNameList(null); 558 List<RouteLocation> routeList = _train.getRoute().getBlockingOrder(); 559 List<Car> carList = carManager.getByTrainDestinationList(_train); 560 List<Car> carsDone = new ArrayList<>(); 561 for (Track track : tracks) { 562 for (RouteLocation rld : routeList) { 563 for (Car car : carList) { 564 if (carsDone.contains(car)) { 565 continue; 566 } 567 // note that a car in train doesn't have a track assignment 568 if (car.getTrack() == null) { 569 continue; 570 } 571 // do local move later 572 if (car.isLocalMove() && rl == rld) { 573 continue; 574 } 575 if (Setup.isSortByTrackNameEnabled() && 576 !car.getTrack().getSplitName().equals(track.getSplitName())) { 577 continue; 578 } 579 // determine if car is a pick up from the right track 580 // caboose or FRED is placed at end of the train 581 // passenger cars are already blocked in the car list 582 // passenger cars with negative block numbers are placed at 583 // the front of the train, positive numbers at the end of 584 // the train. 585 if (TrainCommon.isNextCar(car, rl, rld)) { 586 // yes we have a pick up 587 pWorkPanes.setVisible(true); 588 pickupPane.setVisible(true); 589 if (!rollingStock.contains(car)) { 590 rollingStock.add(car); 591 car.addPropertyChangeListener(this); 592 } 593 // did we already process this car? 594 if (checkBoxes.containsKey("p" + car.getId())) { 595 if (isSetMode && !checkBoxes.get("p" + car.getId()).isSelected()) { 596 // change to set button so user can remove car 597 // from train 598 pPickups.add(addSet(car)); 599 } else { 600 pPickups.add(checkBoxes.get("p" + car.getId())); 601 } 602 // figure out the checkbox text, either single car 603 // or utility 604 } else { 605 String text; 606 if (car.isUtility()) { 607 text = trainCommon.pickupUtilityCars(carList, car, isManifest, 608 !TrainCommon.IS_TWO_COLUMN_TRACK); 609 if (text == null) { 610 continue; // this car type has already been processed 611 } 612 } else { 613 text = trainCommon.pickupCar(car, isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK); 614 } 615 JCheckBox checkBox = new JCheckBox(text); 616 setCheckBoxFont(checkBox, Setup.getPickupColor()); 617 addCheckBoxAction(checkBox); 618 pPickups.add(checkBox); 619 checkBoxes.put("p" + car.getId(), checkBox); 620 } 621 carsDone.add(car); 622 } 623 } 624 } 625 // set outs and local moves 626 for (Car car : carList) { 627 if (carsDone.contains(car)) { 628 continue; 629 } 630 if (car.getRouteDestination() != rl || car.getDestinationTrack() == null) { 631 continue; 632 } 633 // car in train if track null, second check is for yard master window 634 if (car.getTrack() == null || car.getTrack() != null && (car.getRouteLocation() != rl)) { 635 if (Setup.isSortByTrackNameEnabled() && 636 !car.getDestinationTrack().getName().equals(track.getName())) { 637 continue; 638 } 639 // we have set outs 640 pWorkPanes.setVisible(true); 641 setoutPane.setVisible(true); 642 if (!rollingStock.contains(car)) { 643 rollingStock.add(car); 644 car.addPropertyChangeListener(this); 645 } 646 if (checkBoxes.containsKey("s" + car.getId())) { 647 if (isSetMode && !checkBoxes.get("s" + car.getId()).isSelected()) { 648 // change to set button so user can remove car from train 649 pSetouts.add(addSet(car)); 650 } else { 651 pSetouts.add(checkBoxes.get("s" + car.getId())); 652 } 653 } else { 654 String text; 655 if (car.isUtility()) { 656 text = trainCommon.setoutUtilityCars(carList, car, !TrainCommon.LOCAL, isManifest); 657 if (text == null) { 658 continue; // this car type has already been processed 659 } 660 } else { 661 text = trainCommon.dropCar(car, isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK); 662 } 663 JCheckBox checkBox = new JCheckBox(text); 664 setCheckBoxFont(checkBox, Setup.getDropColor()); 665 addCheckBoxAction(checkBox); 666 pSetouts.add(checkBox); 667 checkBoxes.put("s" + car.getId(), checkBox); 668 } 669 // local move? 670 } else if (car.getTrack() != null && 671 car.getRouteLocation() == rl && 672 (!Setup.isSortByTrackNameEnabled() || 673 car.getTrack().getSplitName().equals(track.getSplitName()))) { 674 movePane.setVisible(true); 675 if (!rollingStock.contains(car)) { 676 rollingStock.add(car); 677 car.addPropertyChangeListener(this); 678 } 679 if (checkBoxes.containsKey("m" + car.getId())) { 680 if (isSetMode && !checkBoxes.get("m" + car.getId()).isSelected()) { 681 // change to set button so user can remove car from train 682 pMoves.add(addSet(car)); 683 } else { 684 pMoves.add(checkBoxes.get("m" + car.getId())); 685 } 686 } else { 687 String text; 688 if (car.isUtility()) { 689 text = trainCommon.setoutUtilityCars(carList, car, TrainCommon.LOCAL, isManifest); 690 if (text == null) { 691 continue; // this car type has already been processed 692 } 693 } else { 694 text = trainCommon.localMoveCar(car, isManifest); 695 } 696 JCheckBox checkBox = new JCheckBox(text); 697 setCheckBoxFont(checkBox, Setup.getLocalColor()); 698 addCheckBoxAction(checkBox); 699 pMoves.add(checkBox); 700 checkBoxes.put("m" + car.getId(), checkBox); 701 } 702 carsDone.add(car); 703 } 704 } 705 // if not sorting by track, we're done 706 if (!Setup.isSortByTrackNameEnabled()) { 707 break; 708 } 709 } 710 // pad the panels in case the horizontal scroll bar appears 711 pPickups.add(new JLabel(Space)); 712 pSetouts.add(new JLabel(Space)); 713 pMoves.add(new JLabel(Space)); 714 } 715 716 // replace the car or engine checkbox and text with only the road and number and 717 // a Set button 718 protected JPanel addSet(RollingStock rs) { 719 JPanel pSet = new JPanel(); 720 pSet.setLayout(new GridBagLayout()); 721 JButton setButton = new JButton(Bundle.getMessage("Set")); 722 setButton.setToolTipText(Bundle.getMessage("SetButtonToolTip")); 723 setButton.setName(rs.getId()); 724 setButton.addActionListener((ActionEvent e) -> { 725 if (Car.class.isInstance(rs)) { 726 carSetButtonActionPerfomed(e); 727 } else { 728 engineSetButtonActionPerfomed(e); 729 } 730 }); 731 JLabel label = new JLabel(TrainCommon.padString(rs.toString(), 732 Control.max_len_string_attibute + Control.max_len_string_road_number)); 733 setLabelFont(label); 734 addItem(pSet, label, 0, 0); 735 addItemLeft(pSet, setButton, 1, 0); 736 pSet.setAlignmentX(LEFT_ALIGNMENT); 737 return pSet; 738 } 739 740 protected void setCheckBoxFont(JCheckBox checkBox, Color color) { 741 if (Setup.isTabEnabled()) { 742 Font font = new Font(Setup.getFontName(), Font.PLAIN, checkBox.getFont().getSize()); 743 checkBox.setFont(font); 744 checkBox.setForeground(color); 745 } 746 } 747 748 protected void setLabelFont(JLabel label) { 749 if (Setup.isTabEnabled()) { 750 Font font = new Font(Setup.getFontName(), Font.PLAIN, label.getFont().getSize()); 751 label.setFont(font); 752 } 753 } 754 755 protected void setModifyButtonText() { 756 if (isSetMode) { 757 modifyButton.setText(Bundle.getMessage("Done")); 758 } else { 759 modifyButton.setText(Bundle.getMessage("Modify")); 760 } 761 } 762 763 // returns one of two possible departure strings for a train 764 protected String getStatus(RouteLocation rl, boolean isManifest) { 765 if (rl == _train.getTrainTerminatesRouteLocation()) { 766 return MessageFormat.format(TrainManifestText.getStringTrainTerminates(), 767 new Object[] { _train.getTrainTerminatesName() }); 768 } 769 if (rl != _train.getCurrentRouteLocation() && 770 _train.getExpectedArrivalTime(rl).equals(Train.ALREADY_SERVICED)) { 771 return MessageFormat.format(TrainSwitchListText.getStringTrainDone(), new Object[] { _train.getName() }); 772 } 773 if (!_train.isBuilt() || rl == null) { 774 return _train.getStatus(); 775 } 776 if (Setup.isPrintLoadsAndEmptiesEnabled()) { 777 int emptyCars = _train.getNumberEmptyCarsInTrain(rl); 778 String text; 779 if (isManifest) { 780 text = TrainManifestText.getStringTrainDepartsLoads(); 781 } else { 782 text = TrainSwitchListText.getStringTrainDepartsLoads(); 783 } 784 return MessageFormat.format(text, 785 new Object[] { rl.getSplitName(), rl.getTrainDirectionString(), 786 _train.getNumberCarsInTrain(rl) - emptyCars, emptyCars, _train.getTrainLength(rl), 787 Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl), 788 _train.getTrainTerminatesName(), _train.getName() }); 789 } else { 790 String text; 791 if (isManifest) { 792 text = TrainManifestText.getStringTrainDepartsCars(); 793 } else { 794 text = TrainSwitchListText.getStringTrainDepartsCars(); 795 } 796 return MessageFormat.format(text, 797 new Object[] { rl.getSplitName(), rl.getTrainDirectionString(), 798 _train.getNumberCarsInTrain(rl), _train.getTrainLength(rl), 799 Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl), 800 _train.getTrainTerminatesName(), _train.getName() }); 801 } 802 } 803 804 protected void removeCarFromList(Car car) { 805 checkBoxes.remove("p" + car.getId()); 806 checkBoxes.remove("s" + car.getId()); 807 checkBoxes.remove("m" + car.getId()); 808 log.debug("Car ({}) removed from list", car.toString()); 809 if (car.isUtility()) { 810 clearAndUpdate(); // need to recalculate number of utility cars 811 } 812 } 813 814 protected void clearAndUpdate() { 815 trainCommon.clearUtilityCarTypes(); // reset the utility car counts 816 checkBoxes.clear(); 817 isSetMode = false; 818 update(); 819 } 820 821 // to be overridden 822 protected abstract void update(); 823 824 protected void removePropertyChangeListerners() { 825 rollingStock.stream().forEach((rs) -> { 826 rs.removePropertyChangeListener(this); 827 }); 828 rollingStock.clear(); 829 } 830 831 @Override 832 public void dispose() { 833 _train = null; 834 _location = null; 835 } 836 837 @Override 838 public void propertyChange(PropertyChangeEvent e) { 839 log.debug("Property change {} for: {} old: {} new: {}", e.getPropertyName(), e.getSource(), e.getOldValue(), 840 e.getNewValue()); // NOI18N 841 } 842 843 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CommonConductorYardmasterPanel.class); 844}