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