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