001package jmri.jmrit.operations.trains; 002 003import java.awt.Dimension; 004import java.beans.PropertyChangeListener; 005import java.io.File; 006import java.io.PrintWriter; 007import java.util.*; 008 009import javax.swing.JComboBox; 010 011import org.jdom2.Attribute; 012import org.jdom2.Element; 013 014import jmri.*; 015import jmri.beans.PropertyChangeSupport; 016import jmri.jmrit.operations.OperationsPanel; 017import jmri.jmrit.operations.locations.Location; 018import jmri.jmrit.operations.rollingstock.cars.*; 019import jmri.jmrit.operations.rollingstock.engines.EngineManagerXml; 020import jmri.jmrit.operations.routes.Route; 021import jmri.jmrit.operations.routes.RouteLocation; 022import jmri.jmrit.operations.setup.OperationsSetupXml; 023import jmri.jmrit.operations.setup.Setup; 024import jmri.jmrit.operations.trains.excel.TrainCustomManifest; 025import jmri.jmrit.operations.trains.excel.TrainCustomSwitchList; 026import jmri.jmrit.operations.trains.gui.TrainsTableFrame; 027import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 028import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 029import jmri.script.JmriScriptEngineManager; 030import jmri.util.ColorUtil; 031import jmri.util.swing.JmriJOptionPane; 032 033/** 034 * Manages trains. 035 * 036 * @author Bob Jacobsen Copyright (C) 2003 037 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 038 * 2014 039 */ 040public class TrainManager extends PropertyChangeSupport implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize, PropertyChangeListener { 041 042 static final String NONE = ""; 043 044 // Train frame attributes 045 private String _trainAction = TrainsTableFrame.MOVE; // Trains frame table button action 046 private boolean _buildMessages = true; // when true, show build messages 047 private boolean _buildReport = false; // when true, print/preview build reports 048 private boolean _printPreview = false; // when true, preview train manifest 049 private boolean _openFile = false; // when true, open CSV file manifest 050 private boolean _runFile = false; // when true, run CSV file manifest 051 052 // Conductor attributes 053 private boolean _showLocationHyphenName = false; 054 055 // Trains window row colors 056 private boolean _rowColorManual = true; // when true train colors are manually assigned 057 private String _rowColorBuilt = NONE; // row color when train is built 058 private String _rowColorBuildFailed = NONE; // row color when train build failed 059 private String _rowColorTrainEnRoute = NONE; // row color when train is en route 060 private String _rowColorTerminated = NONE; // row color when train is terminated 061 private String _rowColorReset = NONE; // row color when train is reset 062 063 // Scripts 064 protected List<String> _startUpScripts = new ArrayList<>(); // list of script pathnames to run at start up 065 protected List<String> _shutDownScripts = new ArrayList<>(); // list of script pathnames to run at shut down 066 067 // property changes 068 public static final String LISTLENGTH_CHANGED_PROPERTY = "TrainsListLength"; // NOI18N 069 public static final String PRINTPREVIEW_CHANGED_PROPERTY = "TrainsPrintPreview"; // NOI18N 070 public static final String OPEN_FILE_CHANGED_PROPERTY = "TrainsOpenFile"; // NOI18N 071 public static final String RUN_FILE_CHANGED_PROPERTY = "TrainsRunFile"; // NOI18N 072 public static final String TRAIN_ACTION_CHANGED_PROPERTY = "TrainsAction"; // NOI18N 073 public static final String ROW_COLOR_NAME_CHANGED_PROPERTY = "TrainsRowColorChange"; // NOI18N 074 public static final String TRAINS_BUILT_CHANGED_PROPERTY = "TrainsBuiltChange"; // NOI18N 075 public static final String TRAINS_SHOW_FULL_NAME_PROPERTY = "TrainsShowFullName"; // NOI18N 076 public static final String TRAINS_SAVED_PROPERTY = "TrainsSaved"; // NOI18N 077 078 public TrainManager() { 079 } 080 081 private int _id = 0; // train ids 082 083 /** 084 * Get the number of items in the roster 085 * 086 * @return Number of trains in the roster 087 */ 088 public int getNumEntries() { 089 return _trainHashTable.size(); 090 } 091 092 /** 093 * @return true if build messages are enabled 094 */ 095 public boolean isBuildMessagesEnabled() { 096 return _buildMessages; 097 } 098 099 public void setBuildMessagesEnabled(boolean enable) { 100 boolean old = _buildMessages; 101 _buildMessages = enable; 102 setDirtyAndFirePropertyChange("BuildMessagesEnabled", enable, old); // NOI18N 103 } 104 105 /** 106 * @return true if build reports are enabled 107 */ 108 public boolean isBuildReportEnabled() { 109 return _buildReport; 110 } 111 112 public void setBuildReportEnabled(boolean enable) { 113 boolean old = _buildReport; 114 _buildReport = enable; 115 setDirtyAndFirePropertyChange("BuildReportEnabled", enable, old); // NOI18N 116 } 117 118 /** 119 * @return true if open file is enabled 120 */ 121 public boolean isOpenFileEnabled() { 122 return _openFile; 123 } 124 125 public void setOpenFileEnabled(boolean enable) { 126 boolean old = _openFile; 127 _openFile = enable; 128 setDirtyAndFirePropertyChange(OPEN_FILE_CHANGED_PROPERTY, old ? "true" : "false", enable ? "true" // NOI18N 129 : "false"); // NOI18N 130 } 131 132 /** 133 * @return true if open file is enabled 134 */ 135 public boolean isRunFileEnabled() { 136 return _runFile; 137 } 138 139 public void setRunFileEnabled(boolean enable) { 140 boolean old = _runFile; 141 _runFile = enable; 142 setDirtyAndFirePropertyChange(RUN_FILE_CHANGED_PROPERTY, old ? "true" : "false", enable ? "true" // NOI18N 143 : "false"); // NOI18N 144 } 145 146 /** 147 * @return true if print preview is enabled 148 */ 149 public boolean isPrintPreviewEnabled() { 150 return _printPreview; 151 } 152 153 public void setPrintPreviewEnabled(boolean enable) { 154 boolean old = _printPreview; 155 _printPreview = enable; 156 setDirtyAndFirePropertyChange(PRINTPREVIEW_CHANGED_PROPERTY, old ? "Preview" : "Print", // NOI18N 157 enable ? "Preview" : "Print"); // NOI18N 158 } 159 160 /** 161 * When true show entire location name including hyphen 162 * 163 * @return true when showing entire location name 164 */ 165 public boolean isShowLocationHyphenNameEnabled() { 166 return _showLocationHyphenName; 167 } 168 169 public void setShowLocationHyphenNameEnabled(boolean enable) { 170 boolean old = _showLocationHyphenName; 171 _showLocationHyphenName = enable; 172 setDirtyAndFirePropertyChange(TRAINS_SHOW_FULL_NAME_PROPERTY, old, enable); 173 } 174 175 public String getTrainsFrameTrainAction() { 176 return _trainAction; 177 } 178 179 public void setTrainsFrameTrainAction(String action) { 180 String old = _trainAction; 181 _trainAction = action; 182 if (!old.equals(action)) { 183 setDirtyAndFirePropertyChange(TRAIN_ACTION_CHANGED_PROPERTY, old, action); 184 } 185 } 186 187 /** 188 * Add a script to run after trains have been loaded 189 * 190 * @param pathname The script's pathname 191 */ 192 public void addStartUpScript(String pathname) { 193 _startUpScripts.add(pathname); 194 setDirtyAndFirePropertyChange("addStartUpScript", pathname, null); // NOI18N 195 } 196 197 public void deleteStartUpScript(String pathname) { 198 _startUpScripts.remove(pathname); 199 setDirtyAndFirePropertyChange("deleteStartUpScript", null, pathname); // NOI18N 200 } 201 202 /** 203 * Gets a list of pathnames to run after trains have been loaded 204 * 205 * @return A list of pathnames to run after trains have been loaded 206 */ 207 public List<String> getStartUpScripts() { 208 return _startUpScripts; 209 } 210 211 public void runStartUpScripts() { 212 // use thread to prevent object (Train) thread lock 213 Thread scripts = jmri.util.ThreadingUtil.newThread(new Runnable() { 214 @Override 215 public void run() { 216 for (String scriptPathName : getStartUpScripts()) { 217 try { 218 JmriScriptEngineManager.getDefault() 219 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathName))); 220 } catch (Exception e) { 221 log.error("Problem with script: {}", scriptPathName); 222 } 223 } 224 } 225 }); 226 scripts.setName("Startup Scripts"); // NOI18N 227 scripts.start(); 228 } 229 230 /** 231 * Add a script to run at shutdown 232 * 233 * @param pathname The script's pathname 234 */ 235 public void addShutDownScript(String pathname) { 236 _shutDownScripts.add(pathname); 237 setDirtyAndFirePropertyChange("addShutDownScript", pathname, null); // NOI18N 238 } 239 240 public void deleteShutDownScript(String pathname) { 241 _shutDownScripts.remove(pathname); 242 setDirtyAndFirePropertyChange("deleteShutDownScript", null, pathname); // NOI18N 243 } 244 245 /** 246 * Gets a list of pathnames to run at shutdown 247 * 248 * @return A list of pathnames to run at shutdown 249 */ 250 public List<String> getShutDownScripts() { 251 return _shutDownScripts; 252 } 253 254 public void runShutDownScripts() { 255 for (String scriptPathName : getShutDownScripts()) { 256 try { 257 JmriScriptEngineManager.getDefault() 258 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathName))); 259 } catch (Exception e) { 260 log.error("Problem with script: {}", scriptPathName); 261 } 262 } 263 } 264 265 /** 266 * Used to determine if a train has any restrictions with regard to car 267 * built dates. 268 * 269 * @return true if there's a restriction 270 */ 271 public boolean isBuiltRestricted() { 272 for (Train train : getList()) { 273 if (!train.getBuiltStartYear().equals(Train.NONE) || !train.getBuiltEndYear().equals(Train.NONE)) { 274 return true; 275 } 276 } 277 return false; 278 } 279 280 /** 281 * Used to determine if a train has any restrictions with regard to car 282 * loads. 283 * 284 * @return true if there's a restriction 285 */ 286 public boolean isLoadRestricted() { 287 for (Train train : getList()) { 288 if (!train.getLoadOption().equals(Train.ALL_LOADS)) { 289 return true; 290 } 291 } 292 return false; 293 } 294 295 /** 296 * Used to determine if a train has any restrictions with regard to car 297 * roads. 298 * 299 * @return true if there's a restriction 300 */ 301 public boolean isCarRoadRestricted() { 302 for (Train train : getList()) { 303 if (!train.getCarRoadOption().equals(Train.ALL_ROADS)) { 304 return true; 305 } 306 } 307 return false; 308 } 309 310 /** 311 * Used to determine if a train has any restrictions with regard to caboose 312 * roads. 313 * 314 * @return true if there's a restriction 315 */ 316 public boolean isCabooseRoadRestricted() { 317 for (Train train : getList()) { 318 if (!train.getCabooseRoadOption().equals(Train.ALL_ROADS)) { 319 return true; 320 } 321 } 322 return false; 323 } 324 325 /** 326 * Used to determine if a train has any restrictions with regard to 327 * Locomotive roads. 328 * 329 * @return true if there's a restriction 330 */ 331 public boolean isLocoRoadRestricted() { 332 for (Train train : getList()) { 333 if (!train.getLocoRoadOption().equals(Train.ALL_ROADS)) { 334 return true; 335 } 336 } 337 return false; 338 } 339 340 /** 341 * Used to determine if a train has any restrictions with regard to car 342 * owners. 343 * 344 * @return true if there's a restriction 345 */ 346 public boolean isOwnerRestricted() { 347 for (Train train : getList()) { 348 if (!train.getOwnerOption().equals(Train.ALL_OWNERS)) { 349 return true; 350 } 351 } 352 return false; 353 } 354 355 public void dispose() { 356 _trainHashTable.clear(); 357 _id = 0; 358 } 359 360 // stores known Train instances by id 361 private final Hashtable<String, Train> _trainHashTable = new Hashtable<>(); 362 363 /** 364 * @param name The train's name. 365 * @return requested Train object or null if none exists 366 */ 367 public Train getTrainByName(String name) { 368 if (!InstanceManager.getDefault(TrainManagerXml.class).isTrainFileLoaded()) { 369 log.error("TrainManager getTrainByName called before trains completely loaded!"); 370 } 371 Train train; 372 Enumeration<Train> en = _trainHashTable.elements(); 373 while (en.hasMoreElements()) { 374 train = en.nextElement(); 375 // windows file names are case independent 376 if (train.getName().toLowerCase().equals(name.toLowerCase())) { 377 return train; 378 } 379 } 380 log.debug("Train ({}) doesn't exist", name); 381 return null; 382 } 383 384 public Train getTrainById(String id) { 385 if (!InstanceManager.getDefault(TrainManagerXml.class).isTrainFileLoaded()) { 386 log.error("TrainManager getTrainById called before trains completely loaded!"); 387 } 388 return _trainHashTable.get(id); 389 } 390 391 /** 392 * Finds an existing train or creates a new train if needed. Requires 393 * train's name and creates a unique id for a new train 394 * 395 * @param name The train's name. 396 * @return new train or existing train 397 */ 398 public Train newTrain(String name) { 399 Train train = getTrainByName(name); 400 if (train == null) { 401 _id++; 402 train = new Train(Integer.toString(_id), name); 403 int oldSize = getNumEntries(); 404 _trainHashTable.put(train.getId(), train); 405 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, 406 getNumEntries()); 407 } 408 return train; 409 } 410 411 /** 412 * Remember a NamedBean Object created outside the manager. 413 * 414 * @param train The Train to be added. 415 */ 416 public void register(Train train) { 417 int oldSize = getNumEntries(); 418 _trainHashTable.put(train.getId(), train); 419 // find last id created 420 int id = Integer.parseInt(train.getId()); 421 if (id > _id) { 422 _id = id; 423 } 424 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, getNumEntries()); 425 } 426 427 /** 428 * Forget a NamedBean Object created outside the manager. 429 * 430 * @param train The Train to delete. 431 */ 432 public void deregister(Train train) { 433 if (train == null) { 434 return; 435 } 436 train.dispose(); 437 int oldSize = getNumEntries(); 438 _trainHashTable.remove(train.getId()); 439 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, getNumEntries()); 440 } 441 442 public void replaceLoad(String type, String oldLoadName, String newLoadName) { 443 for (Train train : getList()) { 444 for (String loadName : train.getLoadNames()) { 445 if (loadName.equals(oldLoadName)) { 446 train.deleteLoadName(oldLoadName); 447 if (newLoadName != null) { 448 train.addLoadName(newLoadName); 449 } 450 } 451 // adjust combination car type and load name 452 String[] splitLoad = loadName.split(CarLoad.SPLIT_CHAR); 453 if (splitLoad.length > 1) { 454 if (splitLoad[0].equals(type) && splitLoad[1].equals(oldLoadName)) { 455 train.deleteLoadName(loadName); 456 if (newLoadName != null) { 457 train.addLoadName(type + CarLoad.SPLIT_CHAR + newLoadName); 458 } 459 } 460 } 461 } 462 } 463 } 464 465 /** 466 * @return true if there's a built train 467 */ 468 public boolean isAnyTrainBuilt() { 469 for (Train train : getList()) { 470 if (train.isBuilt()) { 471 return true; 472 } 473 } 474 return false; 475 } 476 477 /** 478 * @return true if there's a train being built 479 */ 480 public boolean isAnyTrainBuilding() { 481 if (getTrainBuilding() != null) { 482 return true; 483 } 484 return false; 485 } 486 487 public Train getTrainBuilding() { 488 for (Train train : getList()) { 489 if (train.isBuilding()) { 490 log.debug("Train {} is currently building", train.getName()); 491 return train; 492 } 493 } 494 return null; 495 } 496 497 /** 498 * Gets the last train built by departure time. 499 * 500 * @return last train built by departure time, or null if no trains are 501 * built. 502 */ 503 public Train getLastTrainBuiltByDepartureTime() { 504 for (Train train : getTrainsByReverseTimeList()) { 505 if (train.isBuilt() && train.getDepartTimeMinutes() > 0) { 506 return train; 507 } 508 } 509 return null; 510 } 511 512 /** 513 * @param car The car looking for a train. 514 * @param buildReport The optional build report for logging. 515 * @return Train that can service car from its current location to the its 516 * destination. 517 */ 518 public Train getTrainForCar(Car car, PrintWriter buildReport) { 519 return getTrainForCar(car, new ArrayList<>(), buildReport, false); 520 } 521 522 /** 523 * @param car The car looking for a train. 524 * @param excludeTrains The trains not to try. 525 * @param buildReport The optional build report for logging. 526 * @param isExcludeRoutes When true eliminate trains that have the same 527 * route in the exclude trains list. 528 * @return Train that can service car from its current location to the its 529 * destination. 530 */ 531 public Train getTrainForCar(Car car, List<Train> excludeTrains, PrintWriter buildReport, boolean isExcludeRoutes) { 532 addLine(buildReport, TrainCommon.BLANK_LINE); 533 addLine(buildReport, Bundle.getMessage("trainFindForCar", car.toString(), car.getLocationName(), 534 car.getTrackName(), car.getDestinationName(), car.getDestinationTrackName())); 535 536 main: for (Train train : getTrainsByNameList()) { 537 if (excludeTrains.contains(train)) { 538 continue; 539 } 540 if (Setup.isOnlyActiveTrainsEnabled() && !train.isBuildEnabled()) { 541 continue; 542 } 543 if (isExcludeRoutes) { 544 for (Train t : excludeTrains) { 545 if (t != null && train.getRoute() == t.getRoute()) { 546 addLine(buildReport, Bundle.getMessage("trainHasSameRoute", train, t)); 547 continue main; 548 } 549 } 550 } 551 // does this train service this car? 552 if (train.isServiceable(buildReport, car)) { 553 log.debug("Found train ({}) for car ({}) location ({}, {}) destination ({}, {})", train.getName(), 554 car.toString(), car.getLocationName(), car.getTrackName(), car.getDestinationName(), 555 car.getDestinationTrackName()); // NOI18N 556 return train; 557 } 558 } 559 return null; 560 } 561 562 public List<Train> getExcludeTrainListForCar(Car car, PrintWriter buildReport) { 563 List<Train> excludeTrains = new ArrayList<>(); 564 for (Train train : getTrainsByNameList()) { 565 if (Setup.isOnlyActiveTrainsEnabled() && !train.isBuildEnabled()) { 566 addLine(buildReport, Bundle.getMessage("trainRoutingDisabled", train.getName())); 567 excludeTrains.add(train); 568 } else if (!train.isTrainAbleToService(buildReport, car)) { 569 excludeTrains.add(train); 570 } 571 } 572 return excludeTrains; 573 } 574 575 protected static final String SEVEN = Setup.BUILD_REPORT_VERY_DETAILED; 576 577 private void addLine(PrintWriter buildReport, String string) { 578 if (Setup.getRouterBuildReportLevel().equals(SEVEN)) { 579 TrainCommon.addLine(buildReport, SEVEN, string); 580 } 581 } 582 583 /** 584 * Sort by train name 585 * 586 * @return list of trains ordered by name 587 */ 588 public List<Train> getTrainsByNameList() { 589 return getTrainsByList(getList(), GET_TRAIN_NAME); 590 } 591 592 /** 593 * Sort by train departure time 594 * 595 * @return list of trains ordered by departure time 596 */ 597 public List<Train> getTrainsByTimeList() { 598 return getTrainsByIntList(getTrainsByNameList(), GET_TRAIN_TIME); 599 } 600 601 public List<Train> getTrainsByReverseTimeList() { 602 List<Train> out = getTrainsByTimeList(); 603 Collections.reverse(out); 604 return out; 605 } 606 607 /** 608 * Sort by train departure location name 609 * 610 * @return list of trains ordered by departure name 611 */ 612 public List<Train> getTrainsByDepartureList() { 613 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_DEPARTES_NAME); 614 } 615 616 /** 617 * Sort by train termination location name 618 * 619 * @return list of trains ordered by termination name 620 */ 621 public List<Train> getTrainsByTerminatesList() { 622 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_TERMINATES_NAME); 623 } 624 625 /** 626 * Sort by train route name 627 * 628 * @return list of trains ordered by route name 629 */ 630 public List<Train> getTrainsByRouteList() { 631 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_ROUTE_NAME); 632 } 633 634 /** 635 * Sort by train status 636 * 637 * @return list of trains ordered by status 638 */ 639 public List<Train> getTrainsByStatusList() { 640 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_STATUS); 641 } 642 643 /** 644 * Sort by train description 645 * 646 * @return list of trains ordered by train description 647 */ 648 public List<Train> getTrainsByDescriptionList() { 649 return getTrainsByList(getTrainsByTimeList(), GET_TRAIN_DESCRIPTION); 650 } 651 652 /** 653 * Sort by train id 654 * 655 * @return list of trains ordered by id 656 */ 657 public List<Train> getTrainsByIdList() { 658 return getTrainsByIntList(getList(), GET_TRAIN_ID); 659 } 660 661 private List<Train> getTrainsByList(List<Train> sortList, int attribute) { 662 List<Train> out = new ArrayList<>(); 663 for (Train train : sortList) { 664 String trainAttribute = (String) getTrainAttribute(train, attribute); 665 for (int j = 0; j < out.size(); j++) { 666 if (trainAttribute.compareToIgnoreCase((String) getTrainAttribute(out.get(j), attribute)) < 0) { 667 out.add(j, train); 668 break; 669 } 670 } 671 if (!out.contains(train)) { 672 out.add(train); 673 } 674 } 675 return out; 676 } 677 678 private List<Train> getTrainsByIntList(List<Train> sortList, int attribute) { 679 List<Train> out = new ArrayList<>(); 680 for (Train train : sortList) { 681 int trainAttribute = (Integer) getTrainAttribute(train, attribute); 682 for (int j = 0; j < out.size(); j++) { 683 if (trainAttribute < (Integer) getTrainAttribute(out.get(j), attribute)) { 684 out.add(j, train); 685 break; 686 } 687 } 688 if (!out.contains(train)) { 689 out.add(train); 690 } 691 } 692 return out; 693 } 694 695 // the various sort options for trains 696 private static final int GET_TRAIN_DEPARTES_NAME = 0; 697 private static final int GET_TRAIN_NAME = 1; 698 private static final int GET_TRAIN_ROUTE_NAME = 2; 699 private static final int GET_TRAIN_TERMINATES_NAME = 3; 700 private static final int GET_TRAIN_TIME = 4; 701 private static final int GET_TRAIN_STATUS = 5; 702 private static final int GET_TRAIN_ID = 6; 703 private static final int GET_TRAIN_DESCRIPTION = 7; 704 705 private Object getTrainAttribute(Train train, int attribute) { 706 switch (attribute) { 707 case GET_TRAIN_DEPARTES_NAME: 708 return train.getTrainDepartsName(); 709 case GET_TRAIN_NAME: 710 return train.getName(); 711 case GET_TRAIN_ROUTE_NAME: 712 return train.getTrainRouteName(); 713 case GET_TRAIN_TERMINATES_NAME: 714 return train.getTrainTerminatesName(); 715 case GET_TRAIN_TIME: 716 return train.getDepartTimeMinutes(); 717 case GET_TRAIN_STATUS: 718 return train.getStatus(); 719 case GET_TRAIN_ID: 720 return Integer.parseInt(train.getId()); 721 case GET_TRAIN_DESCRIPTION: 722 return train.getDescription(); 723 default: 724 return "unknown"; // NOI18N 725 } 726 } 727 728 public List<Train> getList() { 729 if (!InstanceManager.getDefault(TrainManagerXml.class).isTrainFileLoaded()) { 730 log.error("TrainManager getList called before trains completely loaded!"); 731 } 732 List<Train> out = new ArrayList<>(); 733 Enumeration<Train> en = _trainHashTable.elements(); 734 while (en.hasMoreElements()) { 735 out.add(en.nextElement()); 736 } 737 return out; 738 } 739 740 public JComboBox<Train> getTrainComboBox() { 741 JComboBox<Train> box = new JComboBox<>(); 742 updateTrainComboBox(box); 743 OperationsPanel.padComboBox(box); 744 return box; 745 } 746 747 public void updateTrainComboBox(JComboBox<Train> box) { 748 box.removeAllItems(); 749 box.addItem(null); 750 for (Train train : getTrainsByNameList()) { 751 box.addItem(train); 752 } 753 } 754 755 /** 756 * Update combo box with trains that will service this car 757 * 758 * @param box the combo box to update 759 * @param car the car to be serviced 760 */ 761 public void updateTrainComboBox(JComboBox<Train> box, Car car) { 762 box.removeAllItems(); 763 box.addItem(null); 764 for (Train train : getTrainsByNameList()) { 765 if (train.isServiceable(car)) { 766 box.addItem(train); 767 } 768 } 769 } 770 771 public boolean isRowColorManual() { 772 return _rowColorManual; 773 } 774 775 public void setRowColorsManual(boolean manual) { 776 boolean old = _rowColorManual; 777 _rowColorManual = manual; 778 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, manual); 779 } 780 781 public String getRowColorNameForBuilt() { 782 return _rowColorBuilt; 783 } 784 785 public void setRowColorNameForBuilt(String colorName) { 786 String old = _rowColorBuilt; 787 _rowColorBuilt = colorName; 788 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 789 } 790 791 public String getRowColorNameForBuildFailed() { 792 return _rowColorBuildFailed; 793 } 794 795 public void setRowColorNameForBuildFailed(String colorName) { 796 String old = _rowColorBuildFailed; 797 _rowColorBuildFailed = colorName; 798 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 799 } 800 801 public String getRowColorNameForTrainEnRoute() { 802 return _rowColorTrainEnRoute; 803 } 804 805 public void setRowColorNameForTrainEnRoute(String colorName) { 806 String old = _rowColorTrainEnRoute; 807 _rowColorTrainEnRoute = colorName; 808 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 809 } 810 811 public String getRowColorNameForTerminated() { 812 return _rowColorTerminated; 813 } 814 815 public void setRowColorNameForTerminated(String colorName) { 816 String old = _rowColorTerminated; 817 _rowColorTerminated = colorName; 818 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 819 } 820 821 public String getRowColorNameForReset() { 822 return _rowColorReset; 823 } 824 825 public void setRowColorNameForReset(String colorName) { 826 String old = _rowColorReset; 827 _rowColorReset = colorName; 828 setDirtyAndFirePropertyChange(ROW_COLOR_NAME_CHANGED_PROPERTY, old, colorName); 829 } 830 831 /** 832 * JColorChooser is not a replacement for getRowColorComboBox as it doesn't 833 * support no color as a selection. 834 * 835 * @return the available colors used highlighting table rows including no 836 * color. 837 */ 838 public JComboBox<String> getRowColorComboBox() { 839 JComboBox<String> box = new JComboBox<>(); 840 box.addItem(NONE); 841 box.addItem(ColorUtil.ColorBlack); 842 box.addItem(ColorUtil.ColorRed); 843 box.addItem(ColorUtil.ColorPink); 844 box.addItem(ColorUtil.ColorOrange); 845 box.addItem(ColorUtil.ColorYellow); 846 box.addItem(ColorUtil.ColorGreen); 847 box.addItem(ColorUtil.ColorMagenta); 848 box.addItem(ColorUtil.ColorCyan); 849 box.addItem(ColorUtil.ColorBlue); 850 box.addItem(ColorUtil.ColorGray); 851 return box; 852 } 853 854 /** 855 * Makes a copy of an existing train. 856 * 857 * @param train the train to copy 858 * @param trainName the name of the new train 859 * @return a copy of train 860 */ 861 public Train copyTrain(Train train, String trainName) { 862 Train newTrain = newTrain(trainName); 863 // route, departure time and types 864 newTrain.setRoute(train.getRoute()); 865 newTrain.setTrainSkipsLocations(train.getTrainSkipsLocations()); 866 newTrain.setDepartureTime(train.getDepartureTimeDay(), train.getDepartureTimeHour(), 867 train.getDepartureTimeMinute()); 868 newTrain._typeList.clear(); // remove all types loaded by create 869 newTrain.setTypeNames(train.getTypeNames()); 870 // set road, load, and owner options 871 newTrain.setCarRoadOption(train.getCarRoadOption()); 872 newTrain.setCarRoadNames(train.getCarRoadNames()); 873 newTrain.setCabooseRoadNames(train.getCabooseRoadNames()); 874 newTrain.setLocoRoadOption(train.getLocoRoadOption()); 875 newTrain.setLocoRoadNames(train.getLocoRoadNames()); 876 newTrain.setLoadOption(train.getLoadOption()); 877 newTrain.setLoadNames(train.getLoadNames()); 878 newTrain.setOwnerOption(train.getOwnerOption()); 879 newTrain.setOwnerNames(train.getOwnerNames()); 880 // build dates 881 newTrain.setBuiltStartYear(train.getBuiltStartYear()); 882 newTrain.setBuiltEndYear(train.getBuiltEndYear()); 883 // locos start of route 884 newTrain.setNumberEngines(train.getNumberEngines()); 885 newTrain.setEngineModel(train.getEngineModel()); 886 newTrain.setEngineRoad(train.getEngineRoad()); 887 newTrain.setRequirements(train.getRequirements()); 888 newTrain.setCabooseRoad(train.getCabooseRoad()); 889 // second leg 890 newTrain.setSecondLegNumberEngines(train.getSecondLegNumberEngines()); 891 newTrain.setSecondLegEngineModel(train.getSecondLegEngineModel()); 892 newTrain.setSecondLegEngineRoad(train.getSecondLegEngineRoad()); 893 newTrain.setSecondLegOptions(train.getSecondLegOptions()); 894 newTrain.setSecondLegCabooseRoad(train.getSecondLegCabooseRoad()); 895 newTrain.setSecondLegStartRouteLocation(train.getSecondLegStartRouteLocation()); 896 newTrain.setSecondLegEndRouteLocation(train.getSecondLegEndRouteLocation()); 897 // third leg 898 newTrain.setThirdLegNumberEngines(train.getThirdLegNumberEngines()); 899 newTrain.setThirdLegEngineModel(train.getThirdLegEngineModel()); 900 newTrain.setThirdLegEngineRoad(train.getThirdLegEngineRoad()); 901 newTrain.setThirdLegOptions(train.getThirdLegOptions()); 902 newTrain.setThirdLegCabooseRoad(train.getThirdLegCabooseRoad()); 903 newTrain.setThirdLegStartRouteLocation(train.getThirdLegStartRouteLocation()); 904 newTrain.setThirdLegEndRouteLocation(train.getThirdLegEndRouteLocation()); 905 // scripts 906 for (String scriptName : train.getBuildScripts()) { 907 newTrain.addBuildScript(scriptName); 908 } 909 for (String scriptName : train.getMoveScripts()) { 910 newTrain.addMoveScript(scriptName); 911 } 912 for (String scriptName : train.getTerminationScripts()) { 913 newTrain.addTerminationScript(scriptName); 914 } 915 // manifest options 916 newTrain.setRailroadName(train.getRailroadName()); 917 newTrain.setManifestLogoPathName(train.getManifestLogoPathName()); 918 newTrain.setShowArrivalAndDepartureTimes(train.isShowArrivalAndDepartureTimesEnabled()); 919 // build options 920 newTrain.setAllowLocalMovesEnabled(train.isAllowLocalMovesEnabled()); 921 newTrain.setAllowReturnToStagingEnabled(train.isAllowReturnToStagingEnabled()); 922 newTrain.setAllowThroughCarsEnabled(train.isAllowThroughCarsEnabled()); 923 newTrain.setBuildConsistEnabled(train.isBuildConsistEnabled()); 924 newTrain.setSendCarsWithCustomLoadsToStagingEnabled(train.isSendCarsWithCustomLoadsToStagingEnabled()); 925 newTrain.setBuildTrainNormalEnabled(train.isBuildTrainNormalEnabled()); 926 newTrain.setSendCarsToTerminalEnabled(train.isSendCarsToTerminalEnabled()); 927 newTrain.setServiceAllCarsWithFinalDestinationsEnabled(train.isServiceAllCarsWithFinalDestinationsEnabled()); 928 // comment 929 newTrain.setComment(train.getCommentWithColor()); 930 // description 931 newTrain.setDescription(train.getRawDescription()); 932 return newTrain; 933 } 934 935 /** 936 * Provides a list of trains ordered by arrival time to a location 937 * 938 * @param location The location 939 * @return A list of trains ordered by arrival time. 940 */ 941 public List<Train> getTrainsArrivingThisLocationList(Location location) { 942 // get a list of trains 943 List<Train> out = new ArrayList<>(); 944 List<Integer> arrivalTimes = new ArrayList<>(); 945 for (Train train : getTrainsByTimeList()) { 946 if (!train.isBuilt()) { 947 continue; // train wasn't built so skip 948 } 949 Route route = train.getRoute(); 950 if (route == null) { 951 continue; // no route for this train 952 } 953 for (RouteLocation rl : route.getLocationsBySequenceList()) { 954 if (rl.getSplitName().equals(location.getSplitName())) { 955 int expectedArrivalTime = train.getExpectedTravelTimeInMinutes(rl); 956 // is already serviced then "-1" 957 if (expectedArrivalTime == -1) { 958 out.add(0, train); // place all trains that have already been serviced at the start 959 arrivalTimes.add(0, expectedArrivalTime); 960 } // if the train is in route, then expected arrival time is in minutes 961 else if (train.isTrainEnRoute()) { 962 for (int j = 0; j < out.size(); j++) { 963 Train t = out.get(j); 964 int time = arrivalTimes.get(j); 965 if (t.isTrainEnRoute() && expectedArrivalTime < time) { 966 out.add(j, train); 967 arrivalTimes.add(j, expectedArrivalTime); 968 break; 969 } 970 if (!t.isTrainEnRoute()) { 971 out.add(j, train); 972 arrivalTimes.add(j, expectedArrivalTime); 973 break; 974 } 975 } 976 // Train has not departed 977 } else { 978 for (int j = 0; j < out.size(); j++) { 979 Train t = out.get(j); 980 int time = arrivalTimes.get(j); 981 if (!t.isTrainEnRoute() && expectedArrivalTime < time) { 982 out.add(j, train); 983 arrivalTimes.add(j, expectedArrivalTime); 984 break; 985 } 986 } 987 } 988 if (!out.contains(train)) { 989 out.add(train); 990 arrivalTimes.add(expectedArrivalTime); 991 } 992 break; // done 993 } 994 } 995 } 996 return out; 997 } 998 999 /** 1000 * Loads train icons if needed 1001 */ 1002 public void loadTrainIcons() { 1003 for (Train train : getTrainsByIdList()) { 1004 train.loadTrainIcon(); 1005 } 1006 } 1007 1008 /** 1009 * Sets the switch list status for all built trains. Used for switch lists 1010 * in consolidated mode. 1011 * 1012 * @param status Train.PRINTED, Train.UNKNOWN 1013 */ 1014 public void setTrainsSwitchListStatus(String status) { 1015 for (Train train : getTrainsByTimeList()) { 1016 if (!train.isBuilt()) { 1017 continue; // train isn't built so skip 1018 } 1019 train.setSwitchListStatus(status); 1020 } 1021 } 1022 1023 /** 1024 * Sets all built trains manifests to modified. This causes the train's 1025 * manifest to be recreated. 1026 */ 1027 public void setTrainsModified() { 1028 for (Train train : getTrainsByTimeList()) { 1029 if (!train.isBuilt() || train.isTrainEnRoute()) { 1030 continue; // train wasn't built or in route, so skip 1031 } 1032 train.setModified(true); 1033 } 1034 } 1035 1036 public void buildSelectedTrains(List<Train> trains) { 1037 // use a thread to allow table updates during build 1038 Thread build = jmri.util.ThreadingUtil.newThread(new Runnable() { 1039 @Override 1040 public void run() { 1041 for (Train train : trains) { 1042 if (train.buildIfSelected()) { 1043 continue; 1044 } 1045 if (isBuildMessagesEnabled() && train.isBuildEnabled() && !train.isBuilt()) { 1046 if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("ContinueBuilding"), 1047 Bundle.getMessage("buildFailedMsg", 1048 train.getName()), 1049 JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.NO_OPTION) { 1050 break; 1051 } 1052 } 1053 } 1054 setDirtyAndFirePropertyChange(TRAINS_BUILT_CHANGED_PROPERTY, false, true); 1055 } 1056 }); 1057 build.setName("Build Trains"); // NOI18N 1058 build.start(); 1059 } 1060 1061 /** 1062 * Checks to see if using on time build mode and the train to be built has a 1063 * departure time equal to or after all of the other built trains. 1064 * 1065 * @param train the train wanting to be built 1066 * @return true if okay to build train 1067 */ 1068 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 1069 justification="I18N of warning message") 1070 public boolean checkBuildOrder(Train train) { 1071 if (Setup.isBuildOnTime()) { 1072 Train t = getLastTrainBuiltByDepartureTime(); 1073 if (t != null && train.getDepartTimeMinutes() < t.getDepartTimeMinutes()) { 1074 if (isBuildMessagesEnabled()) { 1075 JmriJOptionPane.showMessageDialog(null, 1076 Bundle.getMessage("TrainBuildTimeError", train.getName(), train.getDepartureTime(), 1077 t.getName(), t.getDepartureTime()), 1078 Bundle.getMessage("TrainBuildTime"), JmriJOptionPane.ERROR_MESSAGE); 1079 } else { 1080 log.error(Bundle.getMessage("TrainBuildTimeError", train.getName(), train.getDepartureTime(), 1081 t.getName(), t.getDepartureTime())); 1082 } 1083 return false; 1084 } 1085 } 1086 return true; 1087 } 1088 1089 public boolean printSelectedTrains(List<Train> trains) { 1090 boolean status = true; 1091 for (Train train : trains) { 1092 if (train.isBuildEnabled()) { 1093 if (train.printManifestIfBuilt()) { 1094 continue; 1095 } 1096 status = false; // failed to print all selected trains 1097 if (isBuildMessagesEnabled()) { 1098 int response = JmriJOptionPane.showConfirmDialog(null, 1099 Bundle.getMessage("NeedToBuildBeforePrinting", 1100 train.getName(), 1101 (isPrintPreviewEnabled() ? Bundle.getMessage("preview") 1102 : Bundle.getMessage("print"))), 1103 Bundle.getMessage("CanNotPrintManifest", 1104 isPrintPreviewEnabled() ? Bundle.getMessage("preview") 1105 : Bundle.getMessage("print")), 1106 JmriJOptionPane.OK_CANCEL_OPTION); 1107 if (response != JmriJOptionPane.OK_OPTION) { 1108 break; 1109 } 1110 } 1111 } 1112 } 1113 return status; 1114 } 1115 1116 public boolean terminateSelectedTrains(List<Train> trains) { 1117 if (!confirmTerminateTrains(trains)) { 1118 return false; 1119 } 1120 boolean status = true; 1121 for (Train train : trains) { 1122 if (train.isBuildEnabled() && train.isBuilt()) { 1123 if (train.isPrinted()) { 1124 train.terminate(); 1125 } else { 1126 status = false; 1127 int response = JmriJOptionPane.showConfirmDialog(null, 1128 Bundle.getMessage("WarningTrainManifestNotPrinted"), 1129 Bundle.getMessage("TerminateTrain", 1130 train.getName(), train.getDescription()), 1131 JmriJOptionPane.YES_NO_CANCEL_OPTION); 1132 if (response == JmriJOptionPane.YES_OPTION) { 1133 train.terminate(); 1134 } 1135 // else Quit? 1136 if (response == JmriJOptionPane.CLOSED_OPTION || response == JmriJOptionPane.CANCEL_OPTION) { 1137 break; 1138 } 1139 } 1140 } 1141 } 1142 return status; 1143 } 1144 1145 private boolean confirmTerminateTrains(List<Train> trains) { 1146 if (isBuildMessagesEnabled()) { 1147 int count = 0; 1148 for (Train train : trains) { 1149 if (train.isBuildEnabled() && train.isBuilt()) { 1150 count += 1; 1151 } 1152 } 1153 int response = JmriJOptionPane.showConfirmDialog(null, 1154 Bundle.getMessage("ConfirmTerminate", count), 1155 Bundle.getMessage("TerminateSelectedTip"), 1156 JmriJOptionPane.YES_NO_OPTION); 1157 if (response == JmriJOptionPane.NO_OPTION) { 1158 return false; 1159 } 1160 } 1161 return true; 1162 } 1163 1164 public void resetTrains() { 1165 int response = JmriJOptionPane.showConfirmDialog(null, 1166 Bundle.getMessage("ConfirmReset"), 1167 Bundle.getMessage("ConfirmReset"), 1168 JmriJOptionPane.YES_NO_OPTION); 1169 if (response == JmriJOptionPane.YES_OPTION) { 1170 for (Train train : getTrainsByReverseTimeList()) { 1171 train.reset(); 1172 } 1173 } 1174 } 1175 1176 public void resetBuildFailedTrains() { 1177 for (Train train : getList()) { 1178 if (train.isBuildFailed()) 1179 train.reset(); 1180 } 1181 } 1182 1183 int _maxTrainNameLength = 0; 1184 1185 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "SLF4J_FORMAT_SHOULD_BE_CONST", 1186 justification = "I18N of Info Message") 1187 public int getMaxTrainNameLength() { 1188 String trainName = ""; 1189 if (_maxTrainNameLength == 0) { 1190 for (Train train : getList()) { 1191 if (train.getName().length() > _maxTrainNameLength) { 1192 trainName = train.getName(); 1193 _maxTrainNameLength = train.getName().length(); 1194 } 1195 } 1196 log.info(Bundle.getMessage("InfoMaxName", trainName, _maxTrainNameLength)); 1197 } 1198 return _maxTrainNameLength; 1199 } 1200 1201 1202 private final Hashtable<String, Integer> _HardcopyWriterHashTable = new Hashtable<>(); 1203 1204 public Integer getHardcopyWriterLineLength(String fontName, Integer fontStyle, Integer fontsize, Dimension pagesize, 1205 boolean isLandscape) { 1206 return _HardcopyWriterHashTable.get(getHardcopyWriterKey(fontName, fontStyle, fontsize, pagesize, isLandscape)); 1207 } 1208 1209 public void setHardcopyWriterLineLength(String fontName, Integer fontStyle, Integer fontsize, Dimension pagesize, 1210 boolean isLandscape, Integer charsPerLine) { 1211 _HardcopyWriterHashTable.put(getHardcopyWriterKey(fontName, fontStyle, fontsize, pagesize, isLandscape), 1212 charsPerLine); 1213 } 1214 1215 private String getHardcopyWriterKey(String fontName, Integer fontStyle, Integer fontsize, Dimension pagesize, 1216 boolean isLandscape) { 1217 return fontName + fontStyle + fontsize + pagesize.width + (isLandscape ? "L" : "P"); 1218 } 1219 1220 public void load(Element root) { 1221 if (root.getChild(Xml.OPTIONS) != null) { 1222 Element options = root.getChild(Xml.OPTIONS); 1223 InstanceManager.getDefault(TrainCustomManifest.class).load(options); 1224 InstanceManager.getDefault(TrainCustomSwitchList.class).load(options); 1225 Element e = options.getChild(Xml.TRAIN_OPTIONS); 1226 Attribute a; 1227 if (e != null) { 1228 if ((a = e.getAttribute(Xml.BUILD_MESSAGES)) != null) { 1229 _buildMessages = a.getValue().equals(Xml.TRUE); 1230 } 1231 if ((a = e.getAttribute(Xml.BUILD_REPORT)) != null) { 1232 _buildReport = a.getValue().equals(Xml.TRUE); 1233 } 1234 if ((a = e.getAttribute(Xml.PRINT_PREVIEW)) != null) { 1235 _printPreview = a.getValue().equals(Xml.TRUE); 1236 } 1237 if ((a = e.getAttribute(Xml.OPEN_FILE)) != null) { 1238 _openFile = a.getValue().equals(Xml.TRUE); 1239 } 1240 if ((a = e.getAttribute(Xml.RUN_FILE)) != null) { 1241 _runFile = a.getValue().equals(Xml.TRUE); 1242 } 1243 // verify that the Trains Window action is valid 1244 if ((a = e.getAttribute(Xml.TRAIN_ACTION)) != null && 1245 (a.getValue().equals(TrainsTableFrame.MOVE) || 1246 a.getValue().equals(TrainsTableFrame.RESET) || 1247 a.getValue().equals(TrainsTableFrame.TERMINATE) || 1248 a.getValue().equals(TrainsTableFrame.CONDUCTOR))) { 1249 _trainAction = a.getValue(); 1250 } 1251 } 1252 1253 // Conductor options 1254 Element eConductorOptions = options.getChild(Xml.CONDUCTOR_OPTIONS); 1255 if (eConductorOptions != null) { 1256 if ((a = eConductorOptions.getAttribute(Xml.SHOW_HYPHEN_NAME)) != null) { 1257 _showLocationHyphenName = a.getValue().equals(Xml.TRUE); 1258 } 1259 } 1260 1261 // Row color options 1262 Element eRowColorOptions = options.getChild(Xml.ROW_COLOR_OPTIONS); 1263 if (eRowColorOptions != null) { 1264 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_MANUAL)) != null) { 1265 _rowColorManual = a.getValue().equals(Xml.TRUE); 1266 } 1267 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_BUILD_FAILED)) != null) { 1268 _rowColorBuildFailed = a.getValue().toLowerCase(); 1269 } 1270 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_BUILT)) != null) { 1271 _rowColorBuilt = a.getValue().toLowerCase(); 1272 } 1273 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_TRAIN_EN_ROUTE)) != null) { 1274 _rowColorTrainEnRoute = a.getValue().toLowerCase(); 1275 } 1276 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_TERMINATED)) != null) { 1277 _rowColorTerminated = a.getValue().toLowerCase(); 1278 } 1279 if ((a = eRowColorOptions.getAttribute(Xml.ROW_COLOR_RESET)) != null) { 1280 _rowColorReset = a.getValue().toLowerCase(); 1281 } 1282 } 1283 1284 // moved to train schedule manager 1285 e = options.getChild(jmri.jmrit.operations.trains.schedules.Xml.TRAIN_SCHEDULE_OPTIONS); 1286 if (e != null) { 1287 if ((a = e.getAttribute(jmri.jmrit.operations.trains.schedules.Xml.ACTIVE_ID)) != null) { 1288 InstanceManager.getDefault(TrainScheduleManager.class).setTrainScheduleActiveId(a.getValue()); 1289 } 1290 } 1291 // check for scripts 1292 if (options.getChild(Xml.SCRIPTS) != null) { 1293 List<Element> lm = options.getChild(Xml.SCRIPTS).getChildren(Xml.START_UP); 1294 for (Element es : lm) { 1295 if ((a = es.getAttribute(Xml.NAME)) != null) { 1296 addStartUpScript(a.getValue()); 1297 } 1298 } 1299 List<Element> lt = options.getChild(Xml.SCRIPTS).getChildren(Xml.SHUT_DOWN); 1300 for (Element es : lt) { 1301 if ((a = es.getAttribute(Xml.NAME)) != null) { 1302 addShutDownScript(a.getValue()); 1303 } 1304 } 1305 } 1306 } 1307 if (root.getChild(Xml.TRAINS) != null) { 1308 List<Element> eTrains = root.getChild(Xml.TRAINS).getChildren(Xml.TRAIN); 1309 log.debug("readFile sees {} trains", eTrains.size()); 1310 for (Element eTrain : eTrains) { 1311 register(new Train(eTrain)); 1312 } 1313 } 1314 } 1315 1316 /** 1317 * Create an XML element to represent this Entry. This member has to remain 1318 * synchronized with the detailed DTD in operations-trains.dtd. 1319 * 1320 * @param root common Element for operations-trains.dtd. 1321 */ 1322 public void store(Element root) { 1323 Element options = new Element(Xml.OPTIONS); 1324 Element e = new Element(Xml.TRAIN_OPTIONS); 1325 e.setAttribute(Xml.BUILD_MESSAGES, isBuildMessagesEnabled() ? Xml.TRUE : Xml.FALSE); 1326 e.setAttribute(Xml.BUILD_REPORT, isBuildReportEnabled() ? Xml.TRUE : Xml.FALSE); 1327 e.setAttribute(Xml.PRINT_PREVIEW, isPrintPreviewEnabled() ? Xml.TRUE : Xml.FALSE); 1328 e.setAttribute(Xml.OPEN_FILE, isOpenFileEnabled() ? Xml.TRUE : Xml.FALSE); 1329 e.setAttribute(Xml.RUN_FILE, isRunFileEnabled() ? Xml.TRUE : Xml.FALSE); 1330 e.setAttribute(Xml.TRAIN_ACTION, getTrainsFrameTrainAction()); 1331 options.addContent(e); 1332 1333 // Conductor options 1334 e = new Element(Xml.CONDUCTOR_OPTIONS); 1335 e.setAttribute(Xml.SHOW_HYPHEN_NAME, isShowLocationHyphenNameEnabled() ? Xml.TRUE : Xml.FALSE); 1336 options.addContent(e); 1337 1338 // Trains table row color options 1339 e = new Element(Xml.ROW_COLOR_OPTIONS); 1340 e.setAttribute(Xml.ROW_COLOR_MANUAL, isRowColorManual() ? Xml.TRUE : Xml.FALSE); 1341 e.setAttribute(Xml.ROW_COLOR_BUILD_FAILED, getRowColorNameForBuildFailed()); 1342 e.setAttribute(Xml.ROW_COLOR_BUILT, getRowColorNameForBuilt()); 1343 e.setAttribute(Xml.ROW_COLOR_TRAIN_EN_ROUTE, getRowColorNameForTrainEnRoute()); 1344 e.setAttribute(Xml.ROW_COLOR_TERMINATED, getRowColorNameForTerminated()); 1345 e.setAttribute(Xml.ROW_COLOR_RESET, getRowColorNameForReset()); 1346 options.addContent(e); 1347 1348 if (getStartUpScripts().size() > 0 || getShutDownScripts().size() > 0) { 1349 // save list of shutdown scripts 1350 Element es = new Element(Xml.SCRIPTS); 1351 for (String scriptName : getStartUpScripts()) { 1352 Element em = new Element(Xml.START_UP); 1353 em.setAttribute(Xml.NAME, scriptName); 1354 es.addContent(em); 1355 } 1356 // save list of termination scripts 1357 for (String scriptName : getShutDownScripts()) { 1358 Element et = new Element(Xml.SHUT_DOWN); 1359 et.setAttribute(Xml.NAME, scriptName); 1360 es.addContent(et); 1361 } 1362 options.addContent(es); 1363 } 1364 1365 InstanceManager.getDefault(TrainCustomManifest.class).store(options); // save custom manifest elements 1366 InstanceManager.getDefault(TrainCustomSwitchList.class).store(options); // save custom switch list elements 1367 1368 root.addContent(options); 1369 1370 Element trains = new Element(Xml.TRAINS); 1371 root.addContent(trains); 1372 // add entries 1373 for (Train train : getTrainsByIdList()) { 1374 trains.addContent(train.store()); 1375 } 1376 firePropertyChange(TRAINS_SAVED_PROPERTY, true, false); 1377 } 1378 1379 /** 1380 * Not currently used. 1381 */ 1382 @Override 1383 public void propertyChange(java.beans.PropertyChangeEvent e) { 1384 log.debug("TrainManager sees property change: {} old: {} new: {}", e.getPropertyName(), e.getOldValue(), 1385 e.getNewValue()); 1386 } 1387 1388 private void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 1389 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 1390 firePropertyChange(p, old, n); 1391 } 1392 1393 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrainManager.class); 1394 1395 @Override 1396 public void initialize() { 1397 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 1398 InstanceManager.getDefault(CarManagerXml.class); // load cars 1399 InstanceManager.getDefault(EngineManagerXml.class); // load engines 1400 InstanceManager.getDefault(TrainManagerXml.class); // load trains 1401 } 1402 1403}