예제 #1
0
/**
 * Frame for adding and editing the train roster for operations.
 *
 * @author Bob Jacobsen Copyright (C) 2001
 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014
 * @version $Revision$
 */
public class TrainsTableFrame extends OperationsFrame implements java.beans.PropertyChangeListener {

  /** */
  private static final long serialVersionUID = 4237149773850338265L;

  public static final String MOVE = Bundle.getMessage("Move");
  public static final String TERMINATE = Bundle.getMessage("Terminate");
  public static final String RESET = Bundle.getMessage("Reset");
  public static final String CONDUCTOR = Bundle.getMessage("Conductor");

  CarManagerXml carManagerXml = CarManagerXml.instance(); // load cars
  EngineManagerXml engineManagerXml = EngineManagerXml.instance(); // load engines
  TrainManager trainManager = TrainManager.instance();
  TrainManagerXml trainManagerXml = TrainManagerXml.instance();
  LocationManager locationManager = LocationManager.instance();

  TrainsTableModel trainsModel;
  TableSorter sorter;
  JTable trainsTable;
  JScrollPane trainsPane;

  // labels
  JLabel numTrains = new JLabel();
  JLabel textTrains = new JLabel(Bundle.getMessage("Trains").toLowerCase());
  JLabel textSep1 = new JLabel("      ");

  // radio buttons
  JRadioButton showTime = new JRadioButton(Bundle.getMessage("Time"));
  JRadioButton showId = new JRadioButton(Bundle.getMessage("Id"));

  JRadioButton moveRB = new JRadioButton(MOVE);
  JRadioButton terminateRB = new JRadioButton(TERMINATE);
  JRadioButton resetRB = new JRadioButton(RESET);
  JRadioButton conductorRB = new JRadioButton(CONDUCTOR);

  // major buttons
  JButton addButton = new JButton(Bundle.getMessage("Add"));
  JButton buildButton = new JButton(Bundle.getMessage("Build"));
  JButton printButton = new JButton(Bundle.getMessage("Print"));
  JButton openFileButton = new JButton(Bundle.getMessage("OpenFile"));
  JButton runFileButton = new JButton(Bundle.getMessage("RunFile"));
  JButton switchListsButton = new JButton(Bundle.getMessage("SwitchLists"));
  JButton terminateButton = new JButton(Bundle.getMessage("Terminate"));
  JButton saveButton = new JButton(Bundle.getMessage("SaveBuilds"));

  // check boxes
  JCheckBox buildMsgBox = new JCheckBox(Bundle.getMessage("BuildMessages"));
  JCheckBox buildReportBox = new JCheckBox(Bundle.getMessage("BuildReport"));
  JCheckBox printPreviewBox = new JCheckBox(Bundle.getMessage("Preview"));
  JCheckBox openFileBox = new JCheckBox(Bundle.getMessage("OpenFile"));
  JCheckBox runFileBox = new JCheckBox(Bundle.getMessage("RunFile"));
  JCheckBox showAllBox = new JCheckBox(Bundle.getMessage("ShowAllTrains"));

  public TrainsTableFrame() {
    super();

    updateTitle();

    // create ShutDownTasks
    createShutDownTask();
    // always check for dirty operations files
    setModifiedFlag(true);

    // general GUI configuration
    getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

    // Set up the jtable in a Scroll Pane..
    trainsModel = new TrainsTableModel();
    sorter = new TableSorter(trainsModel);
    trainsTable = new JTable(sorter);
    sorter.setTableHeader(trainsTable.getTableHeader());
    trainsPane = new JScrollPane(trainsTable);
    trainsModel.initTable(trainsTable, this);

    // Set up the control panel
    // row 1
    JPanel cp1 = new JPanel();
    cp1.setLayout(new BoxLayout(cp1, BoxLayout.X_AXIS));

    JPanel show = new JPanel();
    show.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("ShowClickToSort")));
    show.add(showTime);
    show.add(showId);

    JPanel options = new JPanel();
    options.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Options")));
    options.add(showAllBox);
    options.add(buildMsgBox);
    options.add(buildReportBox);
    options.add(printPreviewBox);
    options.add(openFileBox);
    options.add(runFileBox);

    JPanel action = new JPanel();
    action.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Action")));
    action.add(moveRB);
    action.add(conductorRB);
    action.add(terminateRB);
    action.add(resetRB);

    cp1.add(show);
    cp1.add(options);
    cp1.add(action);

    // tool tips, see setPrintButtonText() for more tool tips
    addButton.setToolTipText(Bundle.getMessage("AddTrain"));
    buildButton.setToolTipText(Bundle.getMessage("BuildSelectedTip"));
    switchListsButton.setToolTipText(Bundle.getMessage("PreviewPrintSwitchLists"));

    terminateButton.setToolTipText(Bundle.getMessage("TerminateSelectedTip"));
    saveButton.setToolTipText(Bundle.getMessage("SaveBuildsTip"));
    openFileButton.setToolTipText(Bundle.getMessage("OpenFileButtonTip"));
    runFileButton.setToolTipText(Bundle.getMessage("RunFileButtonTip"));
    buildMsgBox.setToolTipText(Bundle.getMessage("BuildMessagesTip"));
    printPreviewBox.setToolTipText(Bundle.getMessage("PreviewTip"));
    openFileBox.setToolTipText(Bundle.getMessage("OpenFileTip"));
    runFileBox.setToolTipText(Bundle.getMessage("RunFileTip"));
    showAllBox.setToolTipText(Bundle.getMessage("ShowAllTrainsTip"));

    moveRB.setToolTipText(Bundle.getMessage("MoveTip"));
    terminateRB.setToolTipText(Bundle.getMessage("TerminateTip"));
    resetRB.setToolTipText(Bundle.getMessage("ResetTip"));

    // row 2
    JPanel addTrain = new JPanel();
    addTrain.setBorder(BorderFactory.createTitledBorder(""));
    addTrain.add(numTrains);
    addTrain.add(textTrains);
    addTrain.add(textSep1);
    addTrain.add(addButton);

    numTrains.setText(Integer.toString(trainManager.getNumEntries()));

    JPanel select = new JPanel();
    select.setBorder(BorderFactory.createTitledBorder(""));
    select.add(buildButton);
    select.add(printButton);
    select.add(openFileButton);
    select.add(runFileButton);
    select.add(switchListsButton);
    select.add(terminateButton);

    JPanel save = new JPanel();
    save.setBorder(BorderFactory.createTitledBorder(""));
    save.add(saveButton);

    JPanel cp2 = new JPanel();
    cp2.setLayout(new BoxLayout(cp2, BoxLayout.X_AXIS));
    cp2.add(addTrain);
    cp2.add(select);
    cp2.add(save);

    // place controls in scroll pane
    JPanel controlPanel = new JPanel();
    controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
    controlPanel.add(cp1);
    controlPanel.add(cp2);

    JScrollPane controlPane = new JScrollPane(controlPanel);

    getContentPane().add(trainsPane);
    getContentPane().add(controlPane);

    // setup buttons
    addButtonAction(addButton);
    addButtonAction(buildButton);
    addButtonAction(printButton);
    addButtonAction(openFileButton);
    addButtonAction(runFileButton);
    addButtonAction(switchListsButton);
    addButtonAction(terminateButton);
    addButtonAction(saveButton);

    ButtonGroup showGroup = new ButtonGroup();
    showGroup.add(showTime);
    showGroup.add(showId);
    showTime.setSelected(true);

    ButtonGroup actionGroup = new ButtonGroup();
    actionGroup.add(moveRB);
    actionGroup.add(conductorRB);
    actionGroup.add(terminateRB);
    actionGroup.add(resetRB);

    addRadioButtonAction(showTime);
    addRadioButtonAction(showId);

    addRadioButtonAction(moveRB);
    addRadioButtonAction(terminateRB);
    addRadioButtonAction(resetRB);
    addRadioButtonAction(conductorRB);

    buildMsgBox.setSelected(trainManager.isBuildMessagesEnabled());
    buildReportBox.setSelected(trainManager.isBuildReportEnabled());
    printPreviewBox.setSelected(trainManager.isPrintPreviewEnabled());
    openFileBox.setSelected(trainManager.isOpenFileEnabled());
    runFileBox.setSelected(trainManager.isRunFileEnabled());
    showAllBox.setSelected(trainsModel.isShowAll());

    // show open files only if create csv is enabled
    updateRunAndOpenButtons();

    addCheckBoxAction(buildMsgBox);
    addCheckBoxAction(buildReportBox);
    addCheckBoxAction(printPreviewBox);
    addCheckBoxAction(showAllBox);
    addCheckBoxAction(openFileBox);
    addCheckBoxAction(runFileBox);

    // Set the button text to Print or Preview
    setPrintButtonText();
    // Set the train action button text to Move or Terminate
    setTrainActionButton();

    // build menu
    JMenuBar menuBar = new JMenuBar();
    JMenu toolMenu = new JMenu(Bundle.getMessage("Tools"));
    toolMenu.add(new OptionAction(Bundle.getMessage("TitleOptions")));
    toolMenu.add(new PrintOptionAction());
    toolMenu.add(new BuildReportOptionAction());
    toolMenu.add(new TrainsByCarTypeAction(Bundle.getMessage("TitleModifyTrains")));
    toolMenu.add(new TrainByCarTypeAction(Bundle.getMessage("MenuItemShowCarTypes"), null));
    toolMenu.add(new ChangeDepartureTimesAction(Bundle.getMessage("TitleChangeDepartureTime")));
    toolMenu.add(new TrainsTableSetColorAction());
    toolMenu.add(new TrainsScheduleAction(Bundle.getMessage("TitleTimeTableTrains")));
    toolMenu.add(new TrainCopyAction(Bundle.getMessage("TitleTrainCopy")));
    toolMenu.add(new TrainsScriptAction(Bundle.getMessage("MenuItemScripts"), this));
    toolMenu.add(
        new PrintSavedTrainManifestAction(Bundle.getMessage("MenuItemPrintSavedManifest"), false));
    toolMenu.add(
        new PrintSavedTrainManifestAction(Bundle.getMessage("MenuItemPreviewSavedManifest"), true));
    toolMenu.add(new SetupExcelProgramFrameAction(Bundle.getMessage("MenuItemSetupExcelProgram")));
    toolMenu.add(new ExportTrainRosterAction());
    toolMenu.add(
        new PrintTrainsAction(Bundle.getMessage("MenuItemPrint"), new Frame(), false, this));
    toolMenu.add(
        new PrintTrainsAction(Bundle.getMessage("MenuItemPreview"), new Frame(), true, this));

    menuBar.add(toolMenu);
    menuBar.add(new jmri.jmrit.operations.OperationsMenu());
    setJMenuBar(menuBar);

    // add help menu to window
    addHelpMenu("package.jmri.jmrit.operations.Operations_Trains", true); // NOI18N

    initMinimumSize();

    addHorizontalScrollBarKludgeFix(controlPane, controlPanel);

    // listen for timetable changes
    trainManager.addPropertyChangeListener(this);
    Setup.addPropertyChangeListener(this);
    // listen for location switch list changes
    addPropertyChangeLocations();

    // auto save
    new AutoSave();
  }

  public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) {
    log.debug("radio button activated");
    if (ae.getSource() == showId) {
      trainsModel.setSort(trainsModel.SORTBYID);
    }
    if (ae.getSource() == showTime) {
      trainsModel.setSort(trainsModel.SORTBYTIME);
    }
    if (ae.getSource() == moveRB) {
      trainManager.setTrainsFrameTrainAction(MOVE);
    }
    if (ae.getSource() == terminateRB) {
      trainManager.setTrainsFrameTrainAction(TERMINATE);
    }
    if (ae.getSource() == resetRB) {
      trainManager.setTrainsFrameTrainAction(RESET);
    }
    if (ae.getSource() == conductorRB) {
      trainManager.setTrainsFrameTrainAction(CONDUCTOR);
    }
  }

  TrainSwitchListEditFrame tslef;

  // add, build, print, switch lists, terminate, and save buttons
  public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
    // log.debug("train button activated");
    if (ae.getSource() == addButton) {
      new TrainEditFrame(null);
    }
    if (ae.getSource() == buildButton) {
      // uses a thread which allows table updates during build
      trainManager.buildSelectedTrains(getSortByList());
    }
    if (ae.getSource() == printButton) {
      trainManager.printSelectedTrains(getSortByList());
    }
    if (ae.getSource() == openFileButton) {
      // open the csv files
      List<Train> trains = getSortByList();
      for (Train train : trains) {
        if (train.isBuildEnabled()) {
          if (!train.isBuilt() && trainManager.isBuildMessagesEnabled()) {
            JOptionPane.showMessageDialog(
                this,
                MessageFormat.format(
                    Bundle.getMessage("NeedToBuildBeforeOpenFile"),
                    new Object[] {
                      train.getName(),
                      (trainManager.isPrintPreviewEnabled()
                          ? Bundle.getMessage("preview")
                          : Bundle.getMessage("print"))
                    }),
                MessageFormat.format(
                    Bundle.getMessage("CanNotPrintManifest"),
                    new Object[] {
                      trainManager.isPrintPreviewEnabled()
                          ? Bundle.getMessage("preview")
                          : Bundle.getMessage("print")
                    }),
                JOptionPane.ERROR_MESSAGE);
          } else if (train.isBuilt()) {
            train.openFile();
          }
        }
      }
    }
    if (ae.getSource() == runFileButton) {
      // Processes the CSV Manifest files using an external custom program.
      if (!TrainCustomManifest.manifestCreatorFileExists()) {
        log.warn(
            "Manifest creator file not found!, directory name: "
                + TrainCustomManifest.getDirectoryName()
                + ", file name: "
                + TrainCustomManifest.getFileName()); // NOI18N
        JOptionPane.showMessageDialog(
            this,
            MessageFormat.format(
                Bundle.getMessage("LoadDirectoryNameFileName"),
                new Object[] {
                  TrainCustomManifest.getDirectoryName(), TrainCustomManifest.getFileName()
                }),
            Bundle.getMessage("ManifestCreatorNotFound"),
            JOptionPane.ERROR_MESSAGE);
        return;
      }
      List<Train> trains = getSortByList();
      for (Train train : trains) {
        if (train.isBuildEnabled()) {
          if (!train.isBuilt() && trainManager.isBuildMessagesEnabled()) {
            JOptionPane.showMessageDialog(
                this,
                MessageFormat.format(
                    Bundle.getMessage("NeedToBuildBeforeRunFile"),
                    new Object[] {
                      train.getName(),
                      (trainManager.isPrintPreviewEnabled()
                          ? Bundle.getMessage("preview")
                          : Bundle.getMessage("print"))
                    }),
                MessageFormat.format(
                    Bundle.getMessage("CanNotPrintManifest"),
                    new Object[] {
                      trainManager.isPrintPreviewEnabled()
                          ? Bundle.getMessage("preview")
                          : Bundle.getMessage("print")
                    }),
                JOptionPane.ERROR_MESSAGE);
          } else if (train.isBuilt()) {
            // Make sure our csv manifest file exists for this Train.
            File csvFile = train.createCSVManifestFile();
            // Add it to our collection to be processed.
            TrainCustomManifest.addCVSFile(csvFile);
          }
        }
      }

      // Now run the user specified custom Manifest processor program
      TrainCustomManifest.process();
    }
    if (ae.getSource() == switchListsButton) {
      if (tslef != null) {
        tslef.dispose();
      }
      tslef = new TrainSwitchListEditFrame();
      tslef.initComponents();
    }
    if (ae.getSource() == terminateButton) {
      trainManager.terminateSelectedTrains(getSortByList());
    }
    if (ae.getSource() == saveButton) {
      storeValues();
    }
  }

  int _status = TableSorter.ASCENDING;

  protected String getSortBy() {
    // set the defaults
    String sortBy = TrainsTableModel.TIMECOLUMNNAME;
    _status = TableSorter.ASCENDING;
    // now look to see if a sort is active
    for (int i = 0; i < sorter.getColumnCount(); i++) {
      String name = sorter.getColumnName(i);
      int status = sorter.getSortingStatus(i);
      // log.debug("Column " + name + " status " + status);
      if (status != TableSorter.NOT_SORTED && !name.equals("")) {
        sortBy = name;
        _status = status;
        break;
      }
    }
    return sortBy;
  }

  public List<Train> getSortByList() {
    List<Train> sysList;
    String sortBy = getSortBy();
    if (sortBy.equals(TrainsTableModel.IDCOLUMNNAME)) {
      sysList = trainManager.getTrainsByIdList();
    } else if (sortBy.equals(TrainsTableModel.TIMECOLUMNNAME)) {
      sysList = trainManager.getTrainsByTimeList();
    } else if (sortBy.equals(TrainsTableModel.DEPARTSCOLUMNNAME)) {
      sysList = trainManager.getTrainsByDepartureList();
    } else if (sortBy.equals(TrainsTableModel.TERMINATESCOLUMNNAME)) {
      sysList = trainManager.getTrainsByTerminatesList();
    } else if (sortBy.equals(TrainsTableModel.ROUTECOLUMNNAME)) {
      sysList = trainManager.getTrainsByRouteList();
    } else if (sortBy.equals(TrainsTableModel.STATUSCOLUMNNAME)) {
      sysList = trainManager.getTrainsByStatusList();
    } else if (sortBy.equals(TrainsTableModel.DESCRIPTIONCOLUMNNAME)) {
      sysList = trainManager.getTrainsByDescriptionList();
    } else {
      sysList = trainManager.getTrainsByNameList();
    }
    return sysList;
  }

  // Modifies button text and tool tips
  private void setPrintButtonText() {
    if (printPreviewBox.isSelected()) {
      printButton.setText(Bundle.getMessage("Preview"));
      printButton.setToolTipText(Bundle.getMessage("PreviewSelectedTip"));
      buildReportBox.setToolTipText(Bundle.getMessage("BuildReportPreviewTip"));
    } else {
      printButton.setText(Bundle.getMessage("Print"));
      printButton.setToolTipText(Bundle.getMessage("PrintSelectedTip"));
      buildReportBox.setToolTipText(Bundle.getMessage("BuildReportPrintTip"));
    }
  }

  private void setTrainActionButton() {
    moveRB.setSelected(trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.MOVE));
    terminateRB.setSelected(
        trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.TERMINATE));
    resetRB.setSelected(trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.RESET));
    conductorRB.setSelected(
        trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.CONDUCTOR));
  }

  public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
    if (ae.getSource() == buildMsgBox) {
      trainManager.setBuildMessagesEnabled(buildMsgBox.isSelected());
    }
    if (ae.getSource() == buildReportBox) {
      trainManager.setBuildReportEnabled(buildReportBox.isSelected());
    }
    if (ae.getSource() == printPreviewBox) {
      trainManager.setPrintPreviewEnabled(printPreviewBox.isSelected());
      setPrintButtonText(); // set the button text for Print or Preview
    }
    if (ae.getSource() == openFileBox) {
      trainManager.setOpenFileEnabled(openFileBox.isSelected());
      runFileBox.setSelected(false);
      trainManager.setRunFileEnabled(false);
    }
    if (ae.getSource() == runFileBox) {
      trainManager.setRunFileEnabled(runFileBox.isSelected());
      openFileBox.setSelected(false);
      trainManager.setOpenFileEnabled(false);
    }
    if (ae.getSource() == showAllBox) {
      trainsModel.setShowAll(showAllBox.isSelected());
    }
  }

  private void updateTitle() {
    String title = Bundle.getMessage("TitleTrainsTable");
    TrainSchedule sch =
        TrainScheduleManager.instance().getScheduleById(trainManager.getTrainScheduleActiveId());
    if (sch != null) {
      title = title + " (" + sch.getName() + ")";
    }
    setTitle(title);
  }

  private void updateSwitchListButton() {
    log.debug("update switch list button");
    List<Location> locations = locationManager.getList();
    for (Location location : locations) {
      if (location != null
          && location.isSwitchListEnabled()
          && location.getStatus().equals(Location.MODIFIED)) {
        switchListsButton.setBackground(Color.RED);
        return;
      }
    }
    switchListsButton.setBackground(Color.GREEN);
  }

  // show open files only if create csv is enabled
  private void updateRunAndOpenButtons() {
    openFileBox.setVisible(Setup.isGenerateCsvManifestEnabled());
    openFileButton.setVisible(Setup.isGenerateCsvManifestEnabled());
    runFileBox.setVisible(Setup.isGenerateCsvManifestEnabled());
    runFileButton.setVisible(Setup.isGenerateCsvManifestEnabled());
  }

  private synchronized void addPropertyChangeLocations() {
    List<Location> locations = locationManager.getList();
    for (Location location : locations) {
      location.addPropertyChangeListener(this);
    }
  }

  private synchronized void removePropertyChangeLocations() {
    List<Location> locations = locationManager.getList();
    for (Location location : locations) {
      location.removePropertyChangeListener(this);
    }
  }

  public void dispose() {
    /*
     * all JMRI window position and size are now saved in user preference file
     * trainManager.setTrainsFrameTableColumnWidths(getCurrentTableColumnWidths()); // save column widths
     * trainManager.setTrainsFrame(null);
     */
    trainsModel.dispose();
    trainManager.runShutDownScripts();
    trainManager.removePropertyChangeListener(this);
    Setup.removePropertyChangeListener(this);
    removePropertyChangeLocations();
    super.dispose();
  }

  protected void handleModified() {
    if (Setup.isAutoSaveEnabled()) {
      storeValues();
      return;
    }
    if (OperationsXml.areFilesDirty()) {
      int result =
          javax.swing.JOptionPane.showOptionDialog(
              this,
              Bundle.getMessage("PromptQuitWindowNotWritten"),
              Bundle.getMessage("PromptSaveQuit"),
              javax.swing.JOptionPane.YES_NO_OPTION,
              javax.swing.JOptionPane.WARNING_MESSAGE,
              null, // icon
              new String[] {
                ResourceBundle.getBundle("jmri.util.UtilBundle").getString("WarnYesSave"), // NOI18N
                ResourceBundle.getBundle("jmri.util.UtilBundle").getString("WarnNoClose")
              }, // NOI18N
              ResourceBundle.getBundle("jmri.util.UtilBundle").getString("WarnYesSave"));
      if (result == javax.swing.JOptionPane.NO_OPTION) {
        return;
      }
      // user wants to save
      storeValues();
    }
  }

  protected void storeValues() {
    super.storeValues();
    saveTableDetails(trainsTable);
  }

  public void propertyChange(java.beans.PropertyChangeEvent e) {
    if (Control.showProperty) {
      log.debug(
          "Property change: ({}) old: ({}) new: ({})",
          e.getPropertyName(),
          e.getOldValue(),
          e.getNewValue());
    }
    if (e.getPropertyName().equals(TrainManager.ACTIVE_TRAIN_SCHEDULE_ID)) {
      updateTitle();
    }
    if (e.getPropertyName().equals(Location.STATUS_CHANGED_PROPERTY)
        || e.getPropertyName().equals(Location.SWITCHLIST_CHANGED_PROPERTY)) {
      updateSwitchListButton();
    }
    if (e.getPropertyName().equals(Setup.MANIFEST_CSV_PROPERTY_CHANGE)) {
      updateRunAndOpenButtons();
    }
    if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY)) {
      numTrains.setText(Integer.toString(trainManager.getNumEntries()));
    }
  }

  static Logger log = LoggerFactory.getLogger(TrainsTableFrame.class.getName());
}
/**
 * Frame for adding and editing train schedules (Timetable) for operations.
 *
 * @author Bob Jacobsen Copyright (C) 2001
 * @author Daniel Boudreau Copyright (C) 2010, 2012
 * @version $Revision$
 */
public class TrainsScheduleTableFrame extends OperationsFrame implements PropertyChangeListener {

  // public static SwingShutDownTask trainDirtyTask;

  public static final String NAME = Bundle.getMessage("Name"); // Sort by choices
  public static final String TIME = Bundle.getMessage("Time");

  TrainManager trainManager = TrainManager.instance();
  TrainScheduleManager trainScheduleManager = TrainScheduleManager.instance();
  LocationManager locationManager = LocationManager.instance();

  TrainsScheduleTableModel trainsScheduleModel = new TrainsScheduleTableModel();
  javax.swing.JTable trainsScheduleTable = new javax.swing.JTable(trainsScheduleModel);
  JScrollPane trainsPane;

  // labels
  JLabel textSort = new JLabel(Bundle.getMessage("SortBy"));

  // radio buttons
  JRadioButton sortByName = new JRadioButton(NAME);
  JRadioButton sortByTime = new JRadioButton(TIME);

  JRadioButton noneButton = new JRadioButton(Bundle.getMessage("None"));

  // radio button groups
  ButtonGroup schGroup = new ButtonGroup();

  // major buttons
  JButton selectButton = new JButton(Bundle.getMessage("Select"));
  JButton clearButton = new JButton(Bundle.getMessage("Clear"));

  JButton applyButton = new JButton(Bundle.getMessage("Apply"));
  JButton buildButton = new JButton(Bundle.getMessage("Build"));
  JButton printButton = new JButton(Bundle.getMessage("Print"));
  JButton switchListsButton = new JButton();
  JButton terminateButton = new JButton(Bundle.getMessage("Terminate"));

  JButton activateButton = new JButton(Bundle.getMessage("Activate"));
  JButton saveButton = new JButton(Bundle.getMessage("Save"));

  // check boxes
  // panel
  JPanel schedule = new JPanel();

  // text area
  JTextArea commentTextArea = new JTextArea(2, 70);
  JScrollPane commentScroller =
      new JScrollPane(
          commentTextArea,
          JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
          JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

  public TrainsScheduleTableFrame() {

    // general GUI configuration
    getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

    // Set up the jtable in a Scroll Pane..
    trainsPane = new JScrollPane(trainsScheduleTable);
    trainsPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
    trainsPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    trainsScheduleModel.initTable(trainsScheduleTable, this);

    // row comment
    JPanel pC = new JPanel();
    pC.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment")));
    pC.setLayout(new GridBagLayout());
    addItem(pC, commentScroller, 1, 0);

    // adjust text area width based on window size
    adjustTextAreaColumnWidth(commentScroller, commentTextArea);

    // Set up the control panel
    // row 1
    JPanel cp1 = new JPanel();
    cp1.setLayout(new BoxLayout(cp1, BoxLayout.X_AXIS));

    // row 1
    JPanel sortBy = new JPanel();
    sortBy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SortBy")));
    sortBy.add(sortByTime);
    sortBy.add(sortByName);

    // row 2
    schedule.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Active")));
    updateControlPanel();

    cp1.add(sortBy);
    cp1.add(schedule);

    JPanel pButtons = new JPanel();
    pButtons.setLayout(new BoxLayout(pButtons, BoxLayout.X_AXIS));

    JPanel cp3 = new JPanel();
    cp3.setBorder(BorderFactory.createTitledBorder(""));
    cp3.add(clearButton);
    cp3.add(selectButton);

    JPanel cp4 = new JPanel();
    cp4.setBorder(BorderFactory.createTitledBorder(""));
    cp4.add(applyButton);
    cp4.add(buildButton);
    cp4.add(printButton);
    cp4.add(switchListsButton);
    cp4.add(terminateButton);

    JPanel cp5 = new JPanel();
    cp5.setBorder(BorderFactory.createTitledBorder(""));
    cp5.add(activateButton);
    cp5.add(saveButton);

    pButtons.add(cp3);
    pButtons.add(cp4);
    pButtons.add(cp5);

    // tool tips
    selectButton.setToolTipText(Bundle.getMessage("SelectAllButtonTip"));
    clearButton.setToolTipText(Bundle.getMessage("ClearAllButtonTip"));
    applyButton.setToolTipText(Bundle.getMessage("ApplyButtonTip"));
    buildButton.setToolTipText(Bundle.getMessage("BuildSelectedTip"));
    activateButton.setToolTipText(Bundle.getMessage("ActivateButtonTip"));
    terminateButton.setToolTipText(Bundle.getMessage("TerminateSelectedTip"));

    setPrintButtonText();
    setSwitchListButtonText();

    // place controls in scroll pane
    JPanel controlPanel = new JPanel();
    controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
    controlPanel.add(pC);
    controlPanel.add(cp1);
    controlPanel.add(pButtons);

    JScrollPane controlPane = new JScrollPane(controlPanel);
    // make sure control panel is the right size
    controlPane.setMinimumSize(new Dimension(500, 480));
    controlPane.setMaximumSize(new Dimension(2000, 500));
    controlPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);

    getContentPane().add(trainsPane);
    getContentPane().add(controlPane);

    // setup buttons
    addButtonAction(clearButton);
    addButtonAction(selectButton);
    addButtonAction(applyButton);
    addButtonAction(buildButton);
    addButtonAction(printButton);
    addButtonAction(switchListsButton);
    addButtonAction(terminateButton);
    addButtonAction(activateButton);
    addButtonAction(saveButton);

    ButtonGroup sortGroup = new ButtonGroup();
    sortGroup.add(sortByTime);
    sortGroup.add(sortByName);
    sortByTime.setSelected(true);

    addRadioButtonAction(sortByTime);
    addRadioButtonAction(sortByName);

    addRadioButtonAction(noneButton);

    // build menu
    JMenuBar menuBar = new JMenuBar();
    JMenu toolMenu = new JMenu(Bundle.getMessage("Tools"));
    toolMenu.add(new TrainsScheduleEditAction());
    menuBar.add(toolMenu);
    setJMenuBar(menuBar);

    // add help menu to window
    addHelpMenu("package.jmri.jmrit.operations.Operations_Timetable", true); // NOI18N

    setTitle(Bundle.getMessage("TitleTimeTableTrains"));

    initMinimumSize(new Dimension(Control.panelWidth700, Control.panelHeight500));

    addHorizontalScrollBarKludgeFix(controlPane, controlPanel);

    Setup.addPropertyChangeListener(this);
    trainManager.addPropertyChangeListener(this);
    trainScheduleManager.addPropertyChangeListener(this);
    addPropertyChangeLocations();
    addPropertyChangeTrainSchedules();
  }

  public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) {
    log.debug("radio button activated");
    if (ae.getSource() == sortByName) {
      trainsScheduleModel.setSort(trainsScheduleModel.SORTBYNAME);
    } else if (ae.getSource() == sortByTime) {
      trainsScheduleModel.setSort(trainsScheduleModel.SORTBYTIME);
    } else if (ae.getSource() == noneButton) {
      enableButtons(false);
      commentTextArea.setText(""); // no text for the noneButton
      // must be one of the schedule radio buttons
    } else {
      enableButtons(true);
      // update comment field
      TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId());
      commentTextArea.setText(ts.getComment());
    }
  }

  // add, build, print, switch lists, terminate, and save buttons
  public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
    log.debug("button activated");
    if (ae.getSource() == clearButton) {
      updateCheckboxes(false);
    }
    if (ae.getSource() == selectButton) {
      updateCheckboxes(true);
    }
    if (ae.getSource() == applyButton) {
      applySchedule();
    }
    if (ae.getSource() == buildButton) {
      switchListsButton.setEnabled(false);
      // uses a thread which allows table updates during build
      trainManager.buildSelectedTrains(getSortByList());
    }
    if (ae.getSource() == printButton) {
      trainManager.printSelectedTrains(getSortByList());
    }
    if (ae.getSource() == switchListsButton) {
      trainScheduleManager.buildSwitchLists();
    }
    if (ae.getSource() == terminateButton) {
      trainManager.terminateSelectedTrains(getSortByList());
    }
    if (ae.getSource() == activateButton) {
      trainManager.setTrainSecheduleActiveId(getSelectedScheduleId());
      activateButton.setEnabled(false);
    }
    if (ae.getSource() == saveButton) {
      storeValues();
      if (Setup.isCloseWindowOnSaveEnabled()) {
        dispose();
      }
    }
  }

  /*
   * Update radio button names in the same order as the table
   */
  private void updateControlPanel() {
    schedule.removeAll();
    noneButton.setName(""); // Name holds schedule id for the selected radio button
    noneButton.setSelected(true);
    commentTextArea.setText(""); // no text for the noneButton
    enableButtons(false);
    schedule.add(noneButton);
    schGroup.add(noneButton);

    for (int i = trainsScheduleModel.getFixedColumn();
        i < trainsScheduleModel.getColumnCount();
        i++) {
      log.debug("Column name: {}", trainsScheduleTable.getColumnName(i));
      TrainSchedule ts =
          trainScheduleManager.getScheduleByName(trainsScheduleTable.getColumnName(i));
      if (ts != null) {
        JRadioButton b = new JRadioButton();
        b.setText(ts.getName());
        b.setName(ts.getId());
        schedule.add(b);
        schGroup.add(b);
        addRadioButtonAction(b);
        if (b.getName().equals(trainManager.getTrainScheduleActiveId())) {
          b.setSelected(true);
          enableButtons(true);
          // update comment field
          commentTextArea.setText(ts.getComment());
        }
      }
    }
    schedule.revalidate();
  }

  private void updateCheckboxes(boolean selected) {
    TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId());
    if (ts != null) {
      for (Train train : trainManager.getTrainsByIdList()) {
        if (selected) {
          ts.addTrainId(train.getId());
        } else {
          ts.removeTrainId(train.getId());
        }
      }
    }
  }

  private void applySchedule() {
    TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId());
    if (ts != null) {
      for (Train train : trainManager.getTrainsByIdList()) {
        train.setBuildEnabled(ts.containsTrainId(train.getId()));
      }
    }
  }

  private String getSelectedScheduleId() {
    AbstractButton b;
    Enumeration<AbstractButton> en = schGroup.getElements();
    while (en.hasMoreElements()) {
      b = en.nextElement();
      if (b.isSelected()) {
        log.debug("schedule radio button " + b.getText());
        return b.getName();
      }
    }
    return null;
  }

  private void enableButtons(boolean enable) {
    selectButton.setEnabled(enable);
    clearButton.setEnabled(enable);
    applyButton.setEnabled(enable);
    buildButton.setEnabled(enable);
    printButton.setEnabled(enable);
    switchListsButton.setEnabled(enable);
    terminateButton.setEnabled(enable);

    log.debug(
        "Selected id: {}, Active id: {}",
        getSelectedScheduleId(),
        trainManager.getTrainScheduleActiveId());

    activateButton.setEnabled(
        getSelectedScheduleId() != null
            && !getSelectedScheduleId().equals(trainManager.getTrainScheduleActiveId()));

    commentTextArea.setEnabled(enable);
  }

  private List<Train> getSortByList() {
    if (sortByTime.isSelected()) {
      return trainManager.getTrainsByTimeList();
    } else {
      return trainManager.getTrainsByNameList();
    }
  }

  private void setSwitchListButtonText() {
    if (!Setup.isSwitchListRealTime()) {
      switchListsButton.setText(Bundle.getMessage("Update"));
    } else if (trainManager.isPrintPreviewEnabled()) {
      switchListsButton.setText(Bundle.getMessage("PreviewSwitchLists"));
    } else {
      switchListsButton.setText(Bundle.getMessage("PrintSwitchLists"));
    }
  }

  // Modifies button text and tool tips
  private void setPrintButtonText() {
    if (trainManager.isPrintPreviewEnabled()) {
      printButton.setText(Bundle.getMessage("Preview"));
      printButton.setToolTipText(Bundle.getMessage("PreviewSelectedTip"));
    } else {
      printButton.setText(Bundle.getMessage("Print"));
      printButton.setToolTipText(Bundle.getMessage("PrintSelectedTip"));
    }
  }

  //    private void buildSwitchList() {
  //        TrainSwitchLists trainSwitchLists = new TrainSwitchLists();
  //        for (Location location : locationManager.getLocationsByNameList()) {
  //            if (location.isSwitchListEnabled()) {
  //                trainSwitchLists.buildSwitchList(location);
  //                // // print or print changes
  //                if (Setup.isSwitchListRealTime() &&
  // !location.getStatus().equals(Location.PRINTED)) {
  //                    trainSwitchLists.printSwitchList(location,
  // trainManager.isPrintPreviewEnabled());
  //                }
  //            }
  //        }
  //        // set trains switch lists printed
  //        trainManager.setTrainsSwitchListStatus(Train.PRINTED);
  //    }

  private void updateSwitchListButton() {
    log.debug("update switch list button");
    List<Location> locations = locationManager.getList();
    for (Location location : locations) {
      if (location != null
          && location.isSwitchListEnabled()
          && location.getStatus().equals(Location.MODIFIED)) {
        switchListsButton.setBackground(Color.RED);
        return;
      }
    }
    switchListsButton.setBackground(Color.GREEN);
  }

  protected void storeValues() {
    // Save comment
    TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId());
    if (ts != null) {
      ts.setComment(commentTextArea.getText());
    }
    //        updateControlPanel();
    saveTableDetails(trainsScheduleTable);
    OperationsXml.save();
  }

  public void dispose() {
    Setup.removePropertyChangeListener(this);
    trainManager.removePropertyChangeListener(this);
    trainScheduleManager.removePropertyChangeListener(this);
    removePropertyChangeTrainSchedules();
    removePropertyChangeLocations();
    trainsScheduleModel.dispose();
    super.dispose();
  }

  private void addPropertyChangeLocations() {
    for (Location location : locationManager.getList()) {
      location.addPropertyChangeListener(this);
    }
  }

  private void removePropertyChangeLocations() {
    for (Location location : locationManager.getList()) {
      location.removePropertyChangeListener(this);
    }
  }

  private void addPropertyChangeTrainSchedules() {
    List<TrainSchedule> trainSchedules = trainScheduleManager.getSchedulesByIdList();
    for (TrainSchedule ts : trainSchedules) {
      ts.addPropertyChangeListener(this);
    }
  }

  private void removePropertyChangeTrainSchedules() {
    List<TrainSchedule> trainSchedules = trainScheduleManager.getSchedulesByIdList();
    for (TrainSchedule ts : trainSchedules) {
      ts.removePropertyChangeListener(this);
    }
  }

  public void propertyChange(PropertyChangeEvent e) {
    if (Control.showProperty)
      log.debug(
          "Property change {} old: {} new: {}",
          e.getPropertyName(),
          e.getOldValue(),
          e.getNewValue());
    if (e.getPropertyName().equals(TrainScheduleManager.LISTLENGTH_CHANGED_PROPERTY)
        || e.getPropertyName().equals(TrainSchedule.NAME_CHANGED_PROPERTY)) {
      updateControlPanel();
    }
    if (e.getPropertyName().equals(TrainManager.PRINTPREVIEW_CHANGED_PROPERTY)) {
      setPrintButtonText();
      setSwitchListButtonText();
    }
    if (e.getPropertyName().equals(TrainManager.TRAINS_BUILT_CHANGED_PROPERTY)) {
      switchListsButton.setEnabled(true);
    }
    if (e.getPropertyName().equals(Setup.REAL_TIME_PROPERTY_CHANGE)) {
      setSwitchListButtonText();
    }
    if (e.getPropertyName().equals(Location.STATUS_CHANGED_PROPERTY)
        || e.getPropertyName().equals(Location.SWITCHLIST_CHANGED_PROPERTY)) {
      updateSwitchListButton();
    }
  }

  private static final Logger log =
      LoggerFactory.getLogger(TrainsScheduleTableFrame.class.getName());
}
예제 #3
0
/**
 * Builds a switch list for a location on the railroad
 *
 * @author Daniel Boudreau (C) Copyright 2008, 2011, 2012, 2013, 2015
 * @version $Revision: 21846 $
 */
public class TrainSwitchLists extends TrainCommon {

  TrainManager trainManager = TrainManager.instance();
  private static final char FORM_FEED = '\f';
  private static final boolean IS_PRINT_HEADER = true;

  String messageFormatText = ""; // the text being formated in case there's an exception

  /**
   * Builds a switch list for a location showing the work by train arrival time. If not running in
   * real time, new train work is appended to the end of the file. User has the ability to modify
   * the text of the messages which can cause an IllegalArgumentException. Some messages have more
   * arguments than the default message allowing the user to customize the message to their liking.
   *
   * <p>There also an option to list all of the car work by track name. This option is only
   * available in real time and is shown after the switch list by train.
   *
   * @param location The Location needing a switch list
   */
  @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
      value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE",
      justification = "CarManager only provides Car Objects") // NOI18N
  public void buildSwitchList(Location location) {
    // Append switch list data if not operating in real time
    boolean newTrainsOnly = !Setup.isSwitchListRealTime();
    boolean append = false; // add text to end of file when true
    boolean checkFormFeed = true; // used to determine if FF needed between trains
    if (newTrainsOnly) {
      if (!location.getStatus().equals(Location.MODIFIED)
          && !Setup.isSwitchListAllTrainsEnabled()) {
        return; // nothing to add
      }
      append = location.getSwitchListState() == Location.SW_APPEND;
      if (location.getSwitchListState() != Location.SW_APPEND) {
        location.setSwitchListState(Location.SW_APPEND);
      }
      location.setStatus(Location.UPDATED);
    }

    log.debug("Append: {} for location ({})", append, location.getName());

    // create switch list file
    File file = TrainManagerXml.instance().createSwitchListFile(location.getName());

    PrintWriter fileOut = null;
    try {
      fileOut =
          new PrintWriter(
              new BufferedWriter(
                  new OutputStreamWriter(new FileOutputStream(file, append), "UTF-8")),
              true); // NOI18N
    } catch (IOException e) {
      log.error("Can not open switchlist file: {}", file.getName());
      return;
    }
    try {
      // build header
      if (!append) {
        newLine(fileOut, Setup.getRailroadName());
        newLine(fileOut);
        newLine(
            fileOut,
            MessageFormat.format(
                messageFormatText = TrainSwitchListText.getStringSwitchListFor(),
                new Object[] {splitString(location.getName())}));
        if (!location.getSwitchListComment().equals(Location.NONE)) {
          newLine(fileOut, location.getSwitchListComment());
        }
      }

      String valid =
          MessageFormat.format(
              messageFormatText = TrainManifestText.getStringValid(), new Object[] {getDate(true)});
      if (Setup.isPrintTimetableNameEnabled()) {
        TrainSchedule sch =
            TrainScheduleManager.instance()
                .getScheduleById(trainManager.getTrainScheduleActiveId());
        if (sch != null) {
          valid = valid + " (" + sch.getName() + ")";
        }
      }

      // get a list of trains sorted by arrival time
      List<Train> trains = trainManager.getTrainsArrivingThisLocationList(location);
      for (Train train : trains) {
        if (!train.isBuilt()) {
          continue; // train wasn't built so skip
        }
        if (newTrainsOnly && train.getSwitchListStatus().equals(Train.PRINTED)) {
          continue; // already printed this train
        }
        Route route = train.getRoute();
        if (route == null) {
          continue; // no route for this train
        } // determine if train works this location
        boolean works = isThereWorkAtLocation(train, location);
        if (!works && !Setup.isSwitchListAllTrainsEnabled()) {
          log.debug("No work for train ({}) at location ({})", train.getName(), location.getName());
          continue;
        }
        // we're now going to add to the switch list
        if (checkFormFeed) {
          if (append && !Setup.getSwitchListPageFormat().equals(Setup.PAGE_NORMAL)) {
            fileOut.write(FORM_FEED);
          }
          if (Setup.isPrintValidEnabled()) {
            newLine(fileOut, valid);
          }
        } else if (!Setup.getSwitchListPageFormat().equals(Setup.PAGE_NORMAL)) {
          fileOut.write(FORM_FEED);
        }
        checkFormFeed = false; // done with FF for this train
        // some cars booleans and the number of times this location get's serviced
        pickupCars = false; // when true there was a car pick up
        dropCars = false; // when true there was a car set out
        int stops = 1;
        boolean trainDone = false;
        // get engine and car lists
        List<Engine> engineList = engineManager.getByTrainBlockingList(train);
        List<Car> carList = carManager.getByTrainDestinationList(train);
        List<RouteLocation> routeList = route.getLocationsBySequenceList();
        RouteLocation rlPrevious = null;
        // does the train stop once or more at this location?
        for (RouteLocation rl : routeList) {
          if (!splitString(rl.getName()).equals(splitString(location.getName()))) {
            rlPrevious = rl;
            continue;
          }
          String expectedArrivalTime = train.getExpectedArrivalTime(rl);
          if (expectedArrivalTime.equals(Train.ALREADY_SERVICED)) {
            trainDone = true;
          }
          // first time at this location?
          if (stops == 1) {
            newLine(fileOut);
            newLine(
                fileOut,
                MessageFormat.format(
                    messageFormatText = TrainSwitchListText.getStringScheduledWork(),
                    new Object[] {train.getName(), train.getDescription()}));
            if (train.isTrainEnRoute()) {
              if (!trainDone) {
                newLine(
                    fileOut,
                    MessageFormat.format(
                        messageFormatText = TrainSwitchListText.getStringDepartedExpected(),
                        new Object[] {
                          splitString(train.getTrainDepartsName()),
                          expectedArrivalTime,
                          rl.getTrainDirectionString()
                        }));
              }
            } else if (!train.isLocalSwitcher()) {
              if (rl == train.getRoute().getDepartsRouteLocation()) {
                newLine(
                    fileOut,
                    MessageFormat.format(
                        messageFormatText = TrainSwitchListText.getStringDepartsAt(),
                        new Object[] {
                          splitString(train.getTrainDepartsName()),
                          rl.getTrainDirectionString(),
                          train.getFormatedDepartureTime()
                        }));
              } else {
                newLine(
                    fileOut,
                    MessageFormat.format(
                        messageFormatText = TrainSwitchListText.getStringDepartsAtExpectedArrival(),
                        new Object[] {
                          splitString(train.getTrainDepartsName()),
                          train.getFormatedDepartureTime(),
                          expectedArrivalTime,
                          rl.getTrainDirectionString()
                        }));
              }
            }
          } else {
            // multiple visits to this location
            // Print visit number only if previous location wasn't the same
            if (rlPrevious == null
                || !splitString(rl.getName()).equals(splitString(rlPrevious.getName()))) {
              if (Setup.getSwitchListPageFormat().equals(Setup.PAGE_PER_VISIT)) {
                fileOut.write(FORM_FEED);
              }
              newLine(fileOut);
              if (train.isTrainEnRoute()) {
                if (expectedArrivalTime.equals(Train.ALREADY_SERVICED)) {
                  newLine(
                      fileOut,
                      MessageFormat.format(
                          messageFormatText = TrainSwitchListText.getStringVisitNumberDone(),
                          new Object[] {stops, train.getName(), train.getDescription()}));
                } else if (rl != train.getRoute().getTerminatesRouteLocation()) {
                  newLine(
                      fileOut,
                      MessageFormat.format(
                          messageFormatText = TrainSwitchListText.getStringVisitNumberDeparted(),
                          new Object[] {
                            stops,
                            train.getName(),
                            expectedArrivalTime,
                            rl.getTrainDirectionString(),
                            train.getDescription()
                          }));
                } else {
                  // message: Visit number {0} for train ({1}) expect to arrive in {2}, terminates
                  // {3}
                  newLine(
                      fileOut,
                      MessageFormat.format(
                          messageFormatText =
                              TrainSwitchListText.getStringVisitNumberTerminatesDeparted(),
                          new Object[] {
                            stops,
                            train.getName(),
                            expectedArrivalTime,
                            splitString(rl.getName()),
                            train.getDescription()
                          }));
                }
              } else {
                // train hasn't departed
                if (rl != train.getRoute().getTerminatesRouteLocation()) {
                  newLine(
                      fileOut,
                      MessageFormat.format(
                          messageFormatText = TrainSwitchListText.getStringVisitNumber(),
                          new Object[] {
                            stops,
                            train.getName(),
                            expectedArrivalTime,
                            rl.getTrainDirectionString(),
                            train.getDescription()
                          }));
                } else {
                  newLine(
                      fileOut,
                      MessageFormat.format(
                          messageFormatText = TrainSwitchListText.getStringVisitNumberTerminates(),
                          new Object[] {
                            stops,
                            train.getName(),
                            expectedArrivalTime,
                            splitString(rl.getName()),
                            train.getDescription()
                          }));
                }
              }
            } else {
              stops--; // don't bump stop count, same location
              // Does the train reverse direction?
              if (rl.getTrainDirection() != rlPrevious.getTrainDirection()
                  && !TrainSwitchListText.getStringTrainDirectionChange().equals("")) {
                newLine(
                    fileOut,
                    MessageFormat.format(
                        messageFormatText = TrainSwitchListText.getStringTrainDirectionChange(),
                        new Object[] {
                          train.getName(),
                          rl.getTrainDirectionString(),
                          train.getDescription(),
                          train.getTrainTerminatesName()
                        }));
              }
            }
          }

          rlPrevious =
              rl; // save current location in case there's back to back location with the same name

          // add route comment
          if (Setup.isSwitchListRouteLocationCommentEnabled()
              && !rl.getComment().trim().equals("")) {
            newLine(fileOut, rl.getComment());
          }

          // now print out the work for this location
          if (Setup.getManifestFormat().equals(Setup.STANDARD_FORMAT)) {
            pickupEngines(fileOut, engineList, rl, !IS_MANIFEST);
            // if switcher show loco drop at end of list
            if (train.isLocalSwitcher()) {
              blockCarsByTrack(
                  fileOut, train, carList, routeList, rl, IS_PRINT_HEADER, !IS_MANIFEST);
              dropEngines(fileOut, engineList, rl, !IS_MANIFEST);
            } else {
              dropEngines(fileOut, engineList, rl, !IS_MANIFEST);
              blockCarsByTrack(
                  fileOut, train, carList, routeList, rl, IS_PRINT_HEADER, !IS_MANIFEST);
            }
          } else if (Setup.getManifestFormat().equals(Setup.TWO_COLUMN_FORMAT)) {
            blockLocosTwoColumn(fileOut, engineList, rl, !IS_MANIFEST);
            blockCarsByTrackTwoColumn(
                fileOut, train, carList, routeList, rl, IS_PRINT_HEADER, !IS_MANIFEST);
          } else {
            blockLocosTwoColumn(fileOut, engineList, rl, !IS_MANIFEST);
            blockCarsByTrackNameTwoColumn(
                fileOut, train, carList, routeList, rl, IS_PRINT_HEADER, !IS_MANIFEST);
          }
          if (Setup.isPrintHeadersEnabled()
              || !Setup.getManifestFormat().equals(Setup.STANDARD_FORMAT)) {
            printHorizontalLine(fileOut, !IS_MANIFEST);
          }

          stops++;

          // done with work, now print summary for this location if we're done
          if (rl != train.getRoute().getTerminatesRouteLocation()) {
            RouteLocation nextRl = train.getRoute().getNextRouteLocation(rl);
            if (splitString(rl.getName()).equals(splitString(nextRl.getName()))) {
              continue; // the current location name is the "same" as the next
            }
            // print departure text if not a switcher
            if (!train.isLocalSwitcher()) {
              String trainDeparts = "";
              if (Setup.isPrintLoadsAndEmptiesEnabled()) {
                int emptyCars = train.getNumberEmptyCarsInTrain(rl);
                // Message format: Train departs Boston Westbound with 4 loads, 8 empties, 450 feet,
                // 3000 tons
                trainDeparts =
                    MessageFormat.format(
                        TrainSwitchListText.getStringTrainDepartsLoads(),
                        new Object[] {
                          TrainCommon.splitString(rl.getName()),
                          rl.getTrainDirectionString(),
                          train.getNumberCarsInTrain(rl) - emptyCars,
                          emptyCars,
                          train.getTrainLength(rl),
                          Setup.getLengthUnit().toLowerCase(),
                          train.getTrainWeight(rl),
                          train.getTrainTerminatesName(),
                          train.getName()
                        });
              } else {
                // Message format: Train departs Boston Westbound with 12 cars, 450 feet, 3000 tons
                trainDeparts =
                    MessageFormat.format(
                        TrainSwitchListText.getStringTrainDepartsCars(),
                        new Object[] {
                          TrainCommon.splitString(rl.getName()),
                          rl.getTrainDirectionString(),
                          train.getNumberCarsInTrain(rl),
                          train.getTrainLength(rl),
                          Setup.getLengthUnit().toLowerCase(),
                          train.getTrainWeight(rl),
                          train.getTrainTerminatesName(),
                          train.getName()
                        });
              }
              newLine(fileOut, trainDeparts);
            }
          }
        }
        if (trainDone && !pickupCars && !dropCars) {
          // Default message: Train ({0}) has serviced this location
          newLine(
              fileOut,
              MessageFormat.format(
                  messageFormatText = TrainSwitchListText.getStringTrainDone(),
                  new Object[] {
                    train.getName(), train.getDescription(), splitString(location.getName())
                  }));
        } else {
          if (stops > 1 && !pickupCars) {
            // Default message: No car pick ups for train ({0}) at this location
            newLine(
                fileOut,
                MessageFormat.format(
                    messageFormatText = TrainSwitchListText.getStringNoCarPickUps(),
                    new Object[] {
                      train.getName(), train.getDescription(), splitString(location.getName())
                    }));
          }
          if (stops > 1 && !dropCars) {
            // Default message: No car set outs for train ({0}) at this location
            newLine(
                fileOut,
                MessageFormat.format(
                    messageFormatText = TrainSwitchListText.getStringNoCarDrops(),
                    new Object[] {
                      train.getName(), train.getDescription(), splitString(location.getName())
                    }));
          }
        }
      }

      // now report car movement by tracks at location
      if (Setup.isTrackSummaryEnabled() && Setup.isSwitchListRealTime()) {
        clearUtilityCarTypes(); // list utility cars by quantity
        if (Setup.getSwitchListPageFormat().equals(Setup.PAGE_NORMAL)) {
          newLine(fileOut);
          newLine(fileOut);
        } else {
          fileOut.write(FORM_FEED);
        }
        newLine(
            fileOut,
            MessageFormat.format(
                messageFormatText = TrainSwitchListText.getStringSwitchListByTrack(),
                new Object[] {splitString(location.getName())}));

        // we only need the cars delivered to or at this location
        List<RollingStock> rsList = carManager.getByTrainList();
        List<Car> carList = new ArrayList<Car>();
        for (RollingStock rs : rsList) {
          if ((rs.getLocation() != null
                  && splitString(rs.getLocation().getName())
                      .equals(splitString(location.getName())))
              || (rs.getDestination() != null
                  && splitString(rs.getDestination().getName())
                      .equals(splitString(location.getName())))) carList.add((Car) rs);
        }

        List<String> trackNames =
            new ArrayList<
                String>(); // locations and tracks can have "similar" names, only list track names
                           // once
        for (Location loc : locationManager.getLocationsByNameList()) {
          if (!splitString(loc.getName()).equals(splitString(location.getName()))) continue;
          for (Track track : loc.getTrackByNameList(null)) {
            String trackName = splitString(track.getName());
            if (trackNames.contains(trackName)) continue;
            trackNames.add(trackName);

            String trainName = ""; // for printing train message once
            newLine(fileOut);
            newLine(fileOut, trackName); // print out just the track name
            // now show the cars pickup and holds for this track
            for (Car car : carList) {
              if (!splitString(car.getTrackName()).equals(trackName)) {
                continue;
              }
              // is the car scheduled for pickup?
              if (car.getRouteLocation() != null) {
                if (splitString(car.getRouteLocation().getLocation().getName())
                    .equals(splitString(location.getName()))) {
                  // cars are sorted by train name, print train message once
                  if (!trainName.equals(car.getTrainName())) {
                    trainName = car.getTrainName();
                    newLine(
                        fileOut,
                        MessageFormat.format(
                            messageFormatText = TrainSwitchListText.getStringScheduledWork(),
                            new Object[] {car.getTrainName(), car.getTrain().getDescription()}));
                    printPickupCarHeader(fileOut, !IS_MANIFEST, !IS_TWO_COLUMN_TRACK);
                  }
                  if (car.isUtility()) {
                    pickupUtilityCars(fileOut, carList, car, !IS_MANIFEST);
                  } else {
                    pickUpCar(fileOut, car, !IS_MANIFEST);
                  }
                }
                // car holds
              } else if (car.isUtility()) {
                String s = pickupUtilityCars(carList, car, !IS_MANIFEST, !IS_TWO_COLUMN_TRACK);
                if (s != null) {
                  newLine(
                      fileOut,
                      TrainSwitchListText.getStringHoldCar().split("\\{")[0] + s.trim()); // NOI18N
                }
              } else {
                newLine(
                    fileOut,
                    MessageFormat.format(
                        messageFormatText = TrainSwitchListText.getStringHoldCar(),
                        new Object[] {
                          padAndTruncateString(
                              car.getRoadName(), CarRoads.instance().getMaxNameLength()),
                          padAndTruncateString(
                              TrainCommon.splitString(car.getNumber()),
                              Control.max_len_string_print_road_number),
                          padAndTruncateString(
                              car.getTypeName().split("-")[0],
                              CarTypes.instance().getMaxNameLength()),
                          padAndTruncateString(
                              car.getLength() + LENGTHABV, Control.max_len_string_length_name),
                          padAndTruncateString(
                              car.getLoadName(), CarLoads.instance().getMaxNameLength()),
                          padAndTruncateString(trackName, locationManager.getMaxTrackNameLength()),
                          padAndTruncateString(
                              car.getColor(), CarColors.instance().getMaxNameLength())
                        }));
              }
            }
            // now do set outs at this location
            for (Car car : carList) {
              if (!splitString(car.getDestinationTrackName()).equals(trackName)) {
                continue;
              }
              if (car.getRouteDestination() != null
                  && splitString(car.getRouteDestination().getLocation().getName())
                      .equals(splitString(location.getName()))) {
                // cars are sorted by train name, print train message once
                if (!trainName.equals(car.getTrainName())) {
                  trainName = car.getTrainName();
                  newLine(
                      fileOut,
                      MessageFormat.format(
                          messageFormatText = TrainSwitchListText.getStringScheduledWork(),
                          new Object[] {car.getTrainName(), car.getTrain().getDescription()}));
                  printDropCarHeader(fileOut, !IS_MANIFEST, !IS_TWO_COLUMN_TRACK);
                }
                if (car.isUtility()) {
                  setoutUtilityCars(fileOut, carList, car, !IS_MANIFEST);
                } else {
                  dropCar(fileOut, car, !IS_MANIFEST);
                }
              }
            }
          }
        }
      }

    } catch (IllegalArgumentException e) {
      newLine(
          fileOut,
          MessageFormat.format(
              Bundle.getMessage("ErrorIllegalArgument"),
              new Object[] {Bundle.getMessage("TitleSwitchListText"), e.getLocalizedMessage()}));
      newLine(fileOut, messageFormatText);
      e.printStackTrace();
    }

    // Are there any cars that need to be found?
    addCarsLocationUnknown(fileOut, !IS_MANIFEST);
    fileOut.flush();
    fileOut.close();
  }

  public void printSwitchList(Location location, boolean isPreview) {
    File buildFile = TrainManagerXml.instance().getSwitchListFile(location.getName());
    if (!buildFile.exists()) {
      log.warn("Switch list file missing for location ({})", location.getName());
      return;
    }
    if (isPreview && Setup.isManifestEditorEnabled()) {
      TrainPrintUtilities.openDesktopEditor(buildFile);
    } else {
      TrainPrintUtilities.printReport(
          buildFile,
          location.getName(),
          isPreview,
          Setup.getFontName(),
          false,
          FileUtil.getExternalFilename(Setup.getManifestLogoURL()),
          location.getDefaultPrinterName(),
          Setup.getSwitchListOrientation(),
          Setup.getManifestFontSize());
    }
    if (!isPreview) {
      location.setStatus(Location.PRINTED);
      location.setSwitchListState(Location.SW_PRINTED);
    }
  }

  protected void newLine(PrintWriter file, String string) {
    newLine(file, string, !IS_MANIFEST);
  }

  private static final Logger log = LoggerFactory.getLogger(TrainSwitchLists.class.getName());
}