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