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