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