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