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