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