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 */ 038public abstract class CommonConductorYardmasterPanel extends OperationsPanel implements PropertyChangeListener { 039 040 protected static final boolean IS_MANIFEST = true; 041 042 protected static final String Tab = " "; // used to space out headers 043 protected static final String Space = " "; // used to pad out panels 044 045 protected Location _location = null; 046 protected Train _train = null; 047 048 protected TrainManager trainManager = InstanceManager.getDefault(TrainManager.class); 049 protected EngineManager engManager = InstanceManager.getDefault(EngineManager.class); 050 protected CarManager carManager = InstanceManager.getDefault(CarManager.class); 051 protected TrainCommon trainCommon = new TrainCommon(); 052 053 protected JScrollPane locoPane; 054 protected JScrollPane pickupPane; 055 protected JScrollPane setoutPane; 056 protected JScrollPane movePane; 057 058 // labels 059 protected JLabel textRailRoadName = new JLabel(); 060 protected JLabel textTrainDescription = new JLabel(); 061 protected JLabel textLocationName = new JLabel(); 062 protected JLabel textStatus = new JLabel(); 063 064 // major buttons 065 public JButton selectButton = new JButton(Bundle.getMessage("SelectAll")); 066 public JButton clearButton = new JButton(Bundle.getMessage("ClearAll")); 067 public JButton modifyButton = new JButton(Bundle.getMessage("Modify")); // see setModifyButtonText() 068 public JButton moveButton = new JButton(Bundle.getMessage("Move")); 069 070 // text panes 071 protected JTextPane textLocationCommentPane = new JTextPane(); 072 protected JTextPane textTrainCommentPane = new JTextPane(); 073 protected JTextPane textTrainRouteCommentPane = new JTextPane(); 074 protected JTextPane textTrainRouteLocationCommentPane = new JTextPane(); 075 protected JTextPane textSwitchListCommentPane = new JTextPane(); 076 protected JTextPane textTrainStatusPane = new JTextPane(); 077 078 // panels 079 protected JPanel pRailRoadName = new JPanel(); 080 081 protected JPanel pTrainDescription = new JPanel(); 082 083 protected JPanel pLocationName = new JPanel(); 084 085 protected JPanel pTrackComments = new JPanel(); 086 087 protected JPanel pLocos = new JPanel(); 088 protected JPanel pPickupLocos = new JPanel(); 089 protected JPanel pSetoutLocos = new JPanel(); 090 091 protected JPanel pPickups = new JPanel(); 092 protected JPanel pSetouts = new JPanel(); 093 protected JPanel pWorkPanes = new JPanel(); // place car pick ups and set outs side by side using two columns 094 protected JPanel pMoves = new JPanel(); 095 096 protected JPanel pStatus = new JPanel(); 097 protected JPanel pButtons = new JPanel(); 098 099 // check boxes 100 protected ConcurrentHashMap<String, JCheckBox> checkBoxes = new ConcurrentHashMap<>(); 101 protected List<RollingStock> rollingStock = Collections.synchronizedList(new ArrayList<>()); 102 103 // flags 104 protected boolean isSetMode = false; // when true, cars that aren't selected (checkbox) can be "set" 105 106 public CommonConductorYardmasterPanel() { 107 super(); 108 initComponents(); 109 } 110 111 public void initComponents() { 112 113 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 114 115 locoPane = new JScrollPane(pLocos); 116 locoPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Engines"))); 117 118 pickupPane = new JScrollPane(pPickups); 119 pickupPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Pickup"))); 120 121 setoutPane = new JScrollPane(pSetouts); 122 setoutPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SetOut"))); 123 124 movePane = new JScrollPane(pMoves); 125 movePane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocalMoves"))); 126 127 // Set up the panels 128 pTrackComments.setLayout(new BoxLayout(pTrackComments, BoxLayout.Y_AXIS)); 129 pPickupLocos.setLayout(new BoxLayout(pPickupLocos, BoxLayout.Y_AXIS)); 130 pSetoutLocos.setLayout(new BoxLayout(pSetoutLocos, BoxLayout.Y_AXIS)); 131 pPickups.setLayout(new BoxLayout(pPickups, BoxLayout.Y_AXIS)); 132 pSetouts.setLayout(new BoxLayout(pSetouts, BoxLayout.Y_AXIS)); 133 pMoves.setLayout(new BoxLayout(pMoves, BoxLayout.Y_AXIS)); 134 135 // railroad name 136 pRailRoadName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RailroadName"))); 137 pRailRoadName.add(textRailRoadName); 138 139 // location name 140 pLocationName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location"))); 141 pLocationName.add(textLocationName); 142 143 // location comment 144 textLocationCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocationComment"))); 145 textLocationCommentPane.setBackground(null); 146 textLocationCommentPane.setEditable(false); 147 textLocationCommentPane.setMaximumSize(new Dimension(2000, 200)); 148 149 // train description 150 pTrainDescription.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Description"))); 151 pTrainDescription.add(textTrainDescription); 152 153 // train comment 154 textTrainCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainComment"))); 155 textTrainCommentPane.setBackground(null); 156 textTrainCommentPane.setEditable(false); 157 textTrainCommentPane.setMaximumSize(new Dimension(2000, 200)); 158 159 // train route comment 160 textTrainRouteCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RouteComment"))); 161 textTrainRouteCommentPane.setBackground(null); 162 textTrainRouteCommentPane.setEditable(false); 163 textTrainRouteCommentPane.setMaximumSize(new Dimension(2000, 200)); 164 165 // train route location comment 166 textTrainRouteLocationCommentPane 167 .setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RouteLocationComment"))); 168 textTrainRouteLocationCommentPane.setBackground(null); 169 textTrainRouteLocationCommentPane.setEditable(false); 170 textTrainRouteLocationCommentPane.setMaximumSize(new Dimension(2000, 200)); 171 172 // Switch list location comment 173 textSwitchListCommentPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment"))); 174 textSwitchListCommentPane.setBackground(null); 175 textSwitchListCommentPane.setEditable(false); 176 textSwitchListCommentPane.setMaximumSize(new Dimension(2000, 200)); 177 178 // Train status 179 textTrainStatusPane.setBorder(BorderFactory.createTitledBorder("")); 180 textTrainStatusPane.setBackground(null); 181 textTrainStatusPane.setEditable(false); 182 textTrainStatusPane.setMaximumSize(new Dimension(2000, 200)); 183 184 // row 12 185 pLocos.setLayout(new BoxLayout(pLocos, BoxLayout.Y_AXIS)); 186 pWorkPanes.setLayout(new BoxLayout(pWorkPanes, BoxLayout.Y_AXIS)); 187 188 pLocos.add(pPickupLocos); 189 pLocos.add(pSetoutLocos); 190 pWorkPanes.add(pickupPane); 191 pWorkPanes.add(setoutPane); 192 193 // row 13 194 pStatus.setLayout(new GridBagLayout()); 195 pStatus.setBorder(BorderFactory.createTitledBorder("")); 196 addItem(pStatus, textStatus, 0, 0); 197 198 // row 14 199 pButtons.setLayout(new GridBagLayout()); 200 pButtons.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Work"))); 201 addItem(pButtons, selectButton, 0, 0); 202 addItem(pButtons, clearButton, 1, 0); 203 addItem(pButtons, modifyButton, 2, 0); 204 205 // setup buttons 206 addButtonAction(selectButton); 207 addButtonAction(clearButton); 208 addButtonAction(modifyButton); 209 } 210 211 // Select, Clear, and Set Buttons 212 @Override 213 public void buttonActionPerformed(ActionEvent ae) { 214 if (ae.getSource() == selectButton) { 215 selectCheckboxes(true); 216 } 217 if (ae.getSource() == clearButton) { 218 selectCheckboxes(false); 219 } 220 if (ae.getSource() == modifyButton) { 221 isSetMode = !isSetMode; // toggle setMode 222 update(); 223 // ask if user wants to add cars to train 224 if (isSetMode) { 225 addCarToTrain(); 226 } 227 } 228 check(); 229 } 230 231 protected void initialize() { 232 removePropertyChangeListerners(); 233 pTrackComments.removeAll(); 234 pPickupLocos.removeAll(); 235 pSetoutLocos.removeAll(); 236 pPickups.removeAll(); 237 pSetouts.removeAll(); 238 pMoves.removeAll(); 239 240 // turn everything off and re-enable if needed 241 pWorkPanes.setVisible(false); 242 pickupPane.setVisible(false); 243 setoutPane.setVisible(false); 244 locoPane.setVisible(false); 245 pPickupLocos.setVisible(false); 246 pSetoutLocos.setVisible(false); 247 movePane.setVisible(false); 248 249 textTrainRouteLocationCommentPane.setVisible(false); 250 251 setModifyButtonText(); 252 } 253 254 protected void updateComplete() { 255 pTrackComments.repaint(); 256 pPickupLocos.repaint(); 257 pSetoutLocos.repaint(); 258 pPickups.repaint(); 259 pSetouts.repaint(); 260 pMoves.repaint(); 261 262 pTrackComments.revalidate(); 263 pPickupLocos.revalidate(); 264 pSetoutLocos.revalidate(); 265 pPickups.revalidate(); 266 pSetouts.revalidate(); 267 pMoves.revalidate(); 268 269 selectButton.setEnabled(!checkBoxes.isEmpty() && !isSetMode); 270 clearButton.setEnabled(!checkBoxes.isEmpty() && !isSetMode); 271 check(); 272 273 log.debug("update complete"); 274 } 275 276 private void addCarToTrain() { 277 if (JmriJOptionPane.showConfirmDialog(this, 278 Bundle.getMessage("WantAddCarsToTrain?", _train.getName()), 279 Bundle.getMessage("AddCarsToTrain?"), JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) { 280 new CarsTableFrame(false, textLocationName.getText(), null); 281 } 282 } 283 284 CarSetFrame csf = null; 285 286 // action for set button for a car, opens the set car window 287 public void carSetButtonActionPerfomed(ActionEvent ae) { 288 String name = ((JButton) ae.getSource()).getName(); 289 log.debug("Set button for car {}", name); 290 Car car = carManager.getById(name); 291 if (csf != null) { 292 csf.dispose(); 293 } 294 csf = new CarSetFrame(); 295 csf.initComponents(); 296 csf.load(car); 297 } 298 299 EngineSetFrame esf = null; 300 301 // action for set button for an engine, opens the set engine window 302 public void engineSetButtonActionPerfomed(ActionEvent ae) { 303 String name = ((JButton) ae.getSource()).getName(); 304 log.debug("Set button for loco {}", name); 305 Engine eng = engManager.getById(name); 306 if (esf != null) { 307 esf.dispose(); 308 } 309 esf = new EngineSetFrame(); 310 esf.initComponents(); 311 esf.load(eng); 312 } 313 314 // confirm that all work is done 315 @Override 316 protected void checkBoxActionPerformed(ActionEvent ae) { 317 check(); 318 } 319 320 // Determines if all car checkboxes are selected. Disables the Set button if 321 // all checkbox are selected. 322 protected void check() { 323 Enumeration<JCheckBox> en = checkBoxes.elements(); 324 while (en.hasMoreElements()) { 325 JCheckBox checkBox = en.nextElement(); 326 if (!checkBox.isSelected()) { 327 // log.debug("Checkbox (" + checkBox.getText() + ") isn't selected "); 328 moveButton.setEnabled(false); 329 modifyButton.setEnabled(true); 330 return; 331 } 332 } 333 // all selected, work done! 334 moveButton.setEnabled(_train != null && _train.isBuilt()); 335 modifyButton.setEnabled(false); 336 isSetMode = false; 337 setModifyButtonText(); 338 } 339 340 protected void selectCheckboxes(boolean enable) { 341 Enumeration<JCheckBox> en = checkBoxes.elements(); 342 while (en.hasMoreElements()) { 343 JCheckBox checkBox = en.nextElement(); 344 checkBox.setSelected(enable); 345 } 346 isSetMode = false; 347 } 348 349 protected void loadTrainDescription() { 350 textTrainDescription.setText(TrainCommon.getOnlyText(_train.getDescription())); 351 textTrainDescription.setForeground(TrainCommon.getTextColor(_train.getDescription())); 352 } 353 354 /** 355 * show train comment box only if there's a comment 356 */ 357 protected void loadTrainComment() { 358 if (_train.getComment().equals(Train.NONE)) { 359 textTrainCommentPane.setVisible(false); 360 } else { 361 textTrainCommentPane.setVisible(true); 362 textTrainCommentPane.setText(_train.getComment()); 363 textTrainCommentPane.setForeground(TrainCommon.getTextColor(_train.getCommentWithColor())); 364 } 365 } 366 367 protected void loadRailroadName() { 368 // Does this train have a unique railroad name? 369 if (!_train.getRailroadName().equals(Train.NONE)) { 370 textRailRoadName.setText(TrainCommon.getOnlyText(_train.getRailroadName())); 371 textRailRoadName.setForeground(TrainCommon.getTextColor(_train.getRailroadName())); 372 } else { 373 textRailRoadName.setText(Setup.getRailroadName()); 374 } 375 } 376 377 protected void loadLocationComment(Location location) { 378 textLocationCommentPane 379 .setVisible(!location.getComment().isEmpty() && Setup.isPrintLocationCommentsEnabled()); 380 if (textLocationCommentPane.isVisible()) { 381 textLocationCommentPane.setText(location.getComment()); 382 textLocationCommentPane.setForeground(TrainCommon.getTextColor(location.getCommentWithColor())); 383 } 384 } 385 386 protected void loadLocationSwitchListComment(Location location) { 387 textSwitchListCommentPane.setVisible(!location.getSwitchListComment().isEmpty()); 388 if (textSwitchListCommentPane.isVisible()) { 389 textSwitchListCommentPane.setText(location.getSwitchListComment()); 390 textSwitchListCommentPane.setForeground(TrainCommon.getTextColor(location.getSwitchListCommentWithColor())); 391 } 392 } 393 394 /** 395 * show route comment box only if there's a route comment 396 */ 397 protected void loadRouteComment() { 398 if (_train.getRoute() != null && _train.getRoute().getComment().equals(Route.NONE) || 399 !Setup.isPrintRouteCommentsEnabled()) { 400 textTrainRouteCommentPane.setVisible(false); 401 } else { 402 textTrainRouteCommentPane.setVisible(true); 403 textTrainRouteCommentPane.setText(TrainCommon.getOnlyText(_train.getRoute().getComment())); 404 textTrainRouteCommentPane.setForeground(TrainCommon.getTextColor(_train.getRoute().getComment())); 405 } 406 } 407 408 protected void loadRouteLocationComment(RouteLocation rl) { 409 textTrainRouteLocationCommentPane.setVisible(!rl.getComment().equals(RouteLocation.NONE)); 410 if (textTrainRouteLocationCommentPane.isVisible()) { 411 textTrainRouteLocationCommentPane.setText(rl.getComment()); 412 textTrainRouteLocationCommentPane.setForeground(rl.getCommentColor()); 413 } 414 } 415 416 protected void updateTrackComments(RouteLocation rl, boolean isManifest) { 417 Location location = rl.getLocation(); 418 if (location != null) { 419 List<Track> tracks = location.getTracksByNameList(null); 420 for (Track track : tracks) { 421 if (isManifest && !track.isPrintManifestCommentEnabled() || 422 !isManifest && !track.isPrintSwitchListCommentEnabled()) { 423 continue; 424 } 425 // any pick ups or set outs to this track? 426 boolean pickup = false; 427 boolean setout = false; 428 List<Car> carList = carManager.getByTrainDestinationList(_train); 429 for (Car car : carList) { 430 if (car.getRouteLocation() == rl && car.getTrack() != null && car.getTrack() == track) { 431 pickup = true; 432 } 433 if (car.getRouteDestination() == rl && 434 car.getDestinationTrack() != null && 435 car.getDestinationTrack() == track) { 436 setout = true; 437 } 438 } 439 // display the appropriate comment if there's one 440 if (pickup || setout) { 441 JTextPane commentTextPane = new JTextPane(); 442 if (pickup && setout && !track.getCommentBoth().equals(Track.NONE)) { 443 commentTextPane.setText(track.getCommentBoth()); 444 commentTextPane.setForeground(TrainCommon.getTextColor(track.getCommentBothWithColor())); 445 } else if (pickup && !setout && !track.getCommentPickup().equals(Track.NONE)) { 446 commentTextPane.setText(track.getCommentPickup()); 447 commentTextPane.setForeground(TrainCommon.getTextColor(track.getCommentPickupWithColor())); 448 } else if (!pickup && setout && !track.getCommentSetout().equals(Track.NONE)) { 449 commentTextPane.setText(track.getCommentSetout()); 450 commentTextPane.setForeground(TrainCommon.getTextColor(track.getCommentSetoutWithColor())); 451 } 452 if (!commentTextPane.getText().isEmpty()) { 453 commentTextPane.setBorder( 454 BorderFactory.createTitledBorder(Bundle.getMessage("Comment") + " " + track.getName())); 455 commentTextPane.setBackground(null); 456 commentTextPane.setEditable(false); 457 commentTextPane.setMaximumSize(new Dimension(2000, 200)); 458 pTrackComments.add(commentTextPane); 459 pTrackComments.setVisible(true); 460 } 461 } 462 } 463 } 464 } 465 466 /** 467 * Uses "ep" prefix to denote a checkbox with an engine pick up, and "es" 468 * for an engine set out. 469 * 470 * @param rl The routeLocation to show loco pick ups or set outs. 471 */ 472 protected void updateLocoPanes(RouteLocation rl) { 473 if (Setup.isPrintHeadersEnabled()) { 474 JLabel header = new JLabel(Tab + trainCommon.getPickupEngineHeader(!TrainCommon.IS_TWO_COLUMN_TRACK)); 475 setLabelFont(header); 476 pPickupLocos.add(header); 477 JLabel headerDrop = new JLabel(Tab + trainCommon.getDropEngineHeader(!TrainCommon.IS_TWO_COLUMN_TRACK)); 478 setLabelFont(headerDrop); 479 pSetoutLocos.add(headerDrop); 480 } 481 // check for locos 482 List<Engine> engList = engManager.getByTrainBlockingList(_train); 483 for (Engine engine : engList) { 484 if (engine.getRouteLocation() == rl && engine.getTrack() != null) { 485 locoPane.setVisible(true); 486 pPickupLocos.setVisible(true); 487 rollingStock.add(engine); 488 engine.addPropertyChangeListener(this); 489 JCheckBox checkBox; 490 if (checkBoxes.containsKey("ep" + engine.getId())) { 491 checkBox = checkBoxes.get("ep" + engine.getId()); 492 } else { 493 checkBox = new JCheckBox(trainCommon.pickupEngine(engine, !TrainCommon.IS_TWO_COLUMN_TRACK)); 494 setCheckBoxFont(checkBox, Setup.getPickupEngineColor()); 495 addCheckBoxAction(checkBox); 496 checkBoxes.put("ep" + engine.getId(), checkBox); 497 } 498 if (isSetMode && !checkBox.isSelected()) { 499 pPickupLocos.add(addSet(engine)); 500 } else { 501 pPickupLocos.add(checkBox); 502 } 503 } 504 if (engine.getRouteDestination() == rl) { 505 locoPane.setVisible(true); 506 pSetoutLocos.setVisible(true); 507 rollingStock.add(engine); 508 engine.addPropertyChangeListener(this); 509 JCheckBox checkBox; 510 if (checkBoxes.containsKey("es" + engine.getId())) { 511 checkBox = checkBoxes.get("es" + engine.getId()); 512 } else { 513 checkBox = new JCheckBox(trainCommon.dropEngine(engine, !TrainCommon.IS_TWO_COLUMN_TRACK)); 514 setCheckBoxFont(checkBox, Setup.getDropEngineColor()); 515 addCheckBoxAction(checkBox); 516 checkBoxes.put("es" + engine.getId(), checkBox); 517 } 518 if (isSetMode && !checkBox.isSelected()) { 519 pSetoutLocos.add(addSet(engine)); 520 } else { 521 pSetoutLocos.add(checkBox); 522 } 523 } 524 } 525 // pad the panels in case the horizontal scroll bar appears 526 pPickupLocos.add(new JLabel(Space)); 527 pSetoutLocos.add(new JLabel(Space)); 528 } 529 530 /** 531 * Block cars by track (optional), then pick up and set out for each 532 * location in a train's route. This shows each car with a check box or with 533 * a set button. The set button is displayed when the checkbox isn't 534 * selected and the display is in "set" mode. If the car is a utility. Show 535 * the number of cars that have the same attributes, and not the car's road 536 * and number. Each car is displayed only once in one of three panes. The 537 * three panes are pick up, set out, or local move. To keep track of each 538 * car and which pane to use, they are placed in the list "rollingStock" 539 * with the prefix "p", "s" or "m" and the car's unique id. 540 * 541 * @param rl The RouteLocation 542 * @param isManifest True if manifest, false if switch list 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 departure strings for a train 764 protected String getStatus(RouteLocation rl, boolean isManifest) { 765 String text = ""; // user modified text 766 String status = ""; 767 try { 768 if (rl == _train.getTrainTerminatesRouteLocation()) { 769 status = MessageFormat.format(text = TrainManifestText.getStringTrainTerminates(), 770 new Object[]{_train.getTrainTerminatesName(), 771 _train.getSplitName(), _train.getDescription(), 772 rl.getLocation().getDivisionName()}); 773 } else if (rl != _train.getCurrentRouteLocation() && 774 _train.getExpectedArrivalTime(rl).equals(Train.ALREADY_SERVICED)) { 775 status = MessageFormat.format(text = TrainSwitchListText.getStringTrainDone(), 776 new Object[]{_train.getSplitName(), _train.getDescription(), 777 rl.getSplitName()}); 778 } else if (!_train.isBuilt() || rl == null) { 779 status = _train.getStatus(); 780 } else if (Setup.isPrintLoadsAndEmptiesEnabled()) { 781 int emptyCars = _train.getNumberEmptyCarsInTrain(rl); 782 if (isManifest) { 783 text = TrainManifestText.getStringTrainDepartsLoads(); 784 } else { 785 text = TrainSwitchListText.getStringTrainDepartsLoads(); 786 } 787 status = MessageFormat.format(text, 788 new Object[]{rl.getSplitName(), rl.getTrainDirectionString(), 789 _train.getNumberCarsInTrain(rl) - emptyCars, emptyCars, _train.getTrainLength(rl), 790 Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl), 791 _train.getTrainTerminatesName(), 792 _train.getSplitName()}); 793 } else { 794 if (isManifest) { 795 text = TrainManifestText.getStringTrainDepartsCars(); 796 } else { 797 text = TrainSwitchListText.getStringTrainDepartsCars(); 798 } 799 status = MessageFormat.format(text, 800 new Object[]{rl.getSplitName(), rl.getTrainDirectionString(), 801 _train.getNumberCarsInTrain(rl), _train.getTrainLength(rl), 802 Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl), 803 _train.getTrainTerminatesName(), 804 _train.getSplitName()}); 805 } 806 } catch (IllegalArgumentException e) { 807 log.error("Illegal argument", e); 808 return Bundle.getMessage("ErrorIllegalArgument", text, e.getLocalizedMessage()); 809 } 810 _color = TrainCommon.getTextColor(status); 811 return TrainCommon.getOnlyText(status); 812 } 813 814 private Color _color; 815 protected Color getStatusColor() { 816 return _color; 817 } 818 819 protected void removeCarFromList(Car car) { 820 checkBoxes.remove("p" + car.getId()); 821 checkBoxes.remove("s" + car.getId()); 822 checkBoxes.remove("m" + car.getId()); 823 log.debug("Car ({}) removed from list", car.toString()); 824 if (car.isUtility()) { 825 clearAndUpdate(); // need to recalculate number of utility cars 826 } 827 } 828 829 protected void clearAndUpdate() { 830 trainCommon.clearUtilityCarTypes(); // reset the utility car counts 831 checkBoxes.clear(); 832 isSetMode = false; 833 update(); 834 } 835 836 // to be overridden 837 protected abstract void update(); 838 839 protected void removePropertyChangeListerners() { 840 rollingStock.stream().forEach((rs) -> { 841 rs.removePropertyChangeListener(this); 842 }); 843 rollingStock.clear(); 844 } 845 846 @Override 847 public void dispose() { 848 _train = null; 849 _location = null; 850 } 851 852 @Override 853 public void propertyChange(PropertyChangeEvent e) { 854 log.debug("Property change {} for: {} old: {} new: {}", e.getPropertyName(), e.getSource(), e.getOldValue(), 855 e.getNewValue()); // NOI18N 856 } 857 858 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CommonConductorYardmasterPanel.class); 859}