001package jmri.jmrit.operations.trains.schedules; 002 003import java.awt.*; 004import java.beans.PropertyChangeEvent; 005import java.beans.PropertyChangeListener; 006import java.util.Enumeration; 007import java.util.List; 008 009import javax.swing.*; 010 011import jmri.InstanceManager; 012import jmri.jmrit.operations.OperationsFrame; 013import jmri.jmrit.operations.OperationsXml; 014import jmri.jmrit.operations.locations.Location; 015import jmri.jmrit.operations.locations.LocationManager; 016import jmri.jmrit.operations.setup.Control; 017import jmri.jmrit.operations.setup.Setup; 018import jmri.jmrit.operations.trains.Train; 019import jmri.jmrit.operations.trains.TrainManager; 020import jmri.jmrit.operations.trains.excel.TrainCustomManifest; 021import jmri.swing.JTablePersistenceManager; 022import jmri.util.swing.JmriJOptionPane; 023 024/** 025 * Frame for adding and editing train schedules for operations. 026 * 027 * @author Bob Jacobsen Copyright (C) 2001 028 * @author Daniel Boudreau Copyright (C) 2010, 2012, 2016 029 */ 030public class TrainsScheduleTableFrame extends OperationsFrame implements PropertyChangeListener { 031 032 TrainManager trainManager = InstanceManager.getDefault(TrainManager.class); 033 TrainScheduleManager trainScheduleManager = InstanceManager.getDefault(TrainScheduleManager.class); 034 LocationManager locationManager = InstanceManager.getDefault(LocationManager.class); 035 036 TrainsScheduleTableModel trainsScheduleModel = new TrainsScheduleTableModel(); 037 javax.swing.JTable trainsScheduleTable = new javax.swing.JTable(trainsScheduleModel); 038 JScrollPane trainsPane; 039 040 // labels 041 JLabel textSort = new JLabel(Bundle.getMessage("SortBy")); 042 043 // radio buttons 044 JRadioButton sortByName = new JRadioButton(Bundle.getMessage("Name")); 045 JRadioButton sortByTime = new JRadioButton(Bundle.getMessage("Time")); 046 047 JRadioButton noneButton = new JRadioButton(Bundle.getMessage("None")); 048 JRadioButton anyButton = new JRadioButton(Bundle.getMessage("Any")); 049 050 // radio button groups 051 ButtonGroup schGroup = new ButtonGroup(); 052 053 // major buttons 054 JButton selectButton = new JButton(Bundle.getMessage("SelectAll")); 055 JButton copyButton = new JButton(Bundle.getMessage("ButtonCopy")); 056 JButton clearButton = new JButton(Bundle.getMessage("ClearAll")); 057 058 JButton applyButton = new JButton(Bundle.getMessage("ButtonApply")); 059 JButton buildButton = new JButton(Bundle.getMessage("Build")); 060 JButton printButton = new JButton(Bundle.getMessage("Print")); 061 JButton runFileButton = new JButton(Bundle.getMessage("RunFile")); 062 JButton switchListsButton = new JButton(); 063 JButton terminateButton = new JButton(Bundle.getMessage("Terminate")); 064 065 JButton activateButton = new JButton(Bundle.getMessage("Activate")); 066 JButton saveButton = new JButton(Bundle.getMessage("ButtonSave")); 067 068 // check boxes 069 // panel 070 JPanel schedule = new JPanel(); 071 072 // text area 073 JTextArea commentTextArea = new JTextArea(2, 70); 074 JScrollPane commentScroller = new JScrollPane(commentTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 075 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 076 077 public TrainsScheduleTableFrame() { 078 079 // general GUI configuration 080 getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 081 082 // Set up the jtable in a Scroll Pane.. 083 trainsPane = new JScrollPane(trainsScheduleTable); 084 trainsPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 085 trainsPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 086 trainsScheduleModel.initTable(trainsScheduleTable, this); 087 088 // row comment 089 JPanel pC = new JPanel(); 090 pC.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment"))); 091 pC.setLayout(new GridBagLayout()); 092 addItem(pC, commentScroller, 1, 0); 093 094 // adjust text area width based on window size 095 adjustTextAreaColumnWidth(commentScroller, commentTextArea); 096 097 // Set up the control panel 098 // row 1 099 JPanel cp1 = new JPanel(); 100 cp1.setLayout(new BoxLayout(cp1, BoxLayout.X_AXIS)); 101 102 // row 1 103 JPanel sortBy = new JPanel(); 104 sortBy.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SortBy"))); 105 sortBy.add(sortByTime); 106 sortBy.add(sortByName); 107 108 // row 2 109 schedule.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Active"))); 110 updateControlPanel(); 111 112 cp1.add(sortBy); 113 cp1.add(schedule); 114 115 JPanel pButtons = new JPanel(); 116 pButtons.setLayout(new BoxLayout(pButtons, BoxLayout.X_AXIS)); 117 118 JPanel cp3 = new JPanel(); 119 cp3.setBorder(BorderFactory.createTitledBorder("")); 120 cp3.add(clearButton); 121 cp3.add(copyButton); 122 cp3.add(selectButton); 123 124 JPanel cp4 = new JPanel(); 125 cp4.setBorder(BorderFactory.createTitledBorder("")); 126 cp4.add(applyButton); 127 cp4.add(buildButton); 128 cp4.add(printButton); 129 cp4.add(runFileButton); 130 cp4.add(switchListsButton); 131 cp4.add(terminateButton); 132 133 JPanel cp5 = new JPanel(); 134 cp5.setBorder(BorderFactory.createTitledBorder("")); 135 cp5.add(activateButton); 136 cp5.add(saveButton); 137 138 pButtons.add(cp3); 139 pButtons.add(cp4); 140 pButtons.add(cp5); 141 142 // tool tips 143 selectButton.setToolTipText(Bundle.getMessage("SelectAllButtonTip")); 144 copyButton.setToolTipText(Bundle.getMessage("CopyButtonTip")); 145 clearButton.setToolTipText(Bundle.getMessage("ClearAllButtonTip")); 146 applyButton.setToolTipText(Bundle.getMessage("ApplyButtonTip")); 147 buildButton.setToolTipText(Bundle.getMessage("BuildSelectedTip")); 148 runFileButton.setToolTipText(Bundle.getMessage("RunFileButtonTip")); 149 activateButton.setToolTipText(Bundle.getMessage("ActivateButtonTip")); 150 terminateButton.setToolTipText(Bundle.getMessage("TerminateSelectedTip")); 151 152 setPrintButtonText(); 153 setSwitchListButtonText(); 154 155 // place controls in scroll pane 156 JPanel controlPanel = new JPanel(); 157 controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS)); 158 controlPanel.add(pC); 159 controlPanel.add(cp1); 160 controlPanel.add(pButtons); 161 162 JScrollPane controlPane = new JScrollPane(controlPanel); 163 // make sure control panel is the right size 164 controlPane.setMinimumSize(new Dimension(500, 480)); 165 controlPane.setMaximumSize(new Dimension(2000, 500)); 166 controlPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); 167 168 getContentPane().add(trainsPane); 169 getContentPane().add(controlPane); 170 171 // show run button only if create CSV files is enabled 172 updateRunButton(); 173 174 // setup buttons 175 addButtonAction(clearButton); 176 addButtonAction(copyButton); 177 addButtonAction(selectButton); 178 addButtonAction(applyButton); 179 addButtonAction(buildButton); 180 addButtonAction(printButton); 181 addButtonAction(runFileButton); 182 addButtonAction(switchListsButton); 183 addButtonAction(terminateButton); 184 addButtonAction(activateButton); 185 addButtonAction(saveButton); 186 187 ButtonGroup sortGroup = new ButtonGroup(); 188 sortGroup.add(sortByTime); 189 sortGroup.add(sortByName); 190 sortByTime.setSelected(true); 191 192 addRadioButtonAction(sortByTime); 193 addRadioButtonAction(sortByName); 194 195 addRadioButtonAction(noneButton); 196 addRadioButtonAction(anyButton); 197 198 // tips 199 noneButton.setToolTipText(Bundle.getMessage("NoActiveTip")); 200 anyButton.setToolTipText(Bundle.getMessage("AnyActiveTip")); 201 202 // build menu 203 JMenuBar menuBar = new JMenuBar(); 204 JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools")); 205 toolMenu.add(new TrainsScheduleEditAction()); 206 menuBar.add(toolMenu); 207 setJMenuBar(menuBar); 208 209 // add help menu to window 210 addHelpMenu("package.jmri.jmrit.operations.Operations_TrainSchedules", true); // NOI18N 211 212 setTitle(Bundle.getMessage("TitleScheduleTrains")); 213 214 initMinimumSize(new Dimension(Control.panelWidth700, Control.panelHeight500)); 215 216 addHorizontalScrollBarKludgeFix(controlPane, controlPanel); 217 218 Setup.getDefault().addPropertyChangeListener(this); 219 trainManager.addPropertyChangeListener(this); 220 trainScheduleManager.addPropertyChangeListener(this); 221 addPropertyChangeLocations(); 222 addPropertyChangeTrainSchedules(); 223 } 224 225 @Override 226 public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) { 227 log.debug("radio button activated"); 228 // clear any sorts by column 229 clearTableSort(trainsScheduleTable); 230 if (ae.getSource() == sortByName) { 231 trainsScheduleModel.setSort(trainsScheduleModel.SORTBYNAME); 232 } else if (ae.getSource() == sortByTime) { 233 trainsScheduleModel.setSort(trainsScheduleModel.SORTBYTIME); 234 } else if (ae.getSource() == noneButton || ae.getSource() == anyButton) { 235 enableButtons(false); 236 commentTextArea.setText(""); // no text for the noneButton or anyButton 237 // must be one of the schedule radio buttons 238 } else { 239 enableButtons(true); 240 // update comment field 241 TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId()); 242 commentTextArea.setText(ts.getComment()); 243 } 244 } 245 246 // add, build, print, switch lists, terminate, and save buttons 247 @Override 248 public void buttonActionPerformed(java.awt.event.ActionEvent ae) { 249 log.debug("button activated"); 250 if (ae.getSource() == clearButton) { 251 updateCheckboxes(false); 252 } 253 if (ae.getSource() == selectButton) { 254 updateCheckboxes(true); 255 } 256 if (ae.getSource() == copyButton) { 257 copySchedule(); 258 } 259 if (ae.getSource() == applyButton) { 260 applySchedule(); 261 } 262 if (ae.getSource() == buildButton) { 263 switchListsButton.setEnabled(false); 264 runFileButton.setEnabled(false); 265 // uses a thread which allows table updates during build 266 trainManager.buildSelectedTrains(getSortByList()); 267 } 268 if (ae.getSource() == printButton) { 269 trainManager.printSelectedTrains(getSortByList()); 270 } 271 if (ae.getSource() == runFileButton) { 272 // Processes the CSV Manifest files using an external custom program. 273 if (!InstanceManager.getDefault(TrainCustomManifest.class).doesExcelFileExist()) { 274 log.warn("Manifest creator file not found!, directory path: {}, file name: {}", 275 InstanceManager.getDefault(TrainCustomManifest.class).getDirectoryPathName(), 276 InstanceManager.getDefault(TrainCustomManifest.class).getFileName()); // NOI18N 277 JmriJOptionPane.showMessageDialog(this, 278 Bundle.getMessage("LoadDirectoryNameFileName", 279 InstanceManager.getDefault(TrainCustomManifest.class).getDirectoryPathName(), 280 InstanceManager.getDefault(TrainCustomManifest.class).getFileName()), 281 Bundle.getMessage("ManifestCreatorNotFound"), JmriJOptionPane.ERROR_MESSAGE); 282 return; 283 } 284 List<Train> trains = getSortByList(); 285 for (Train train : trains) { 286 if (train.isBuildEnabled()) { 287 if (!train.isBuilt()) { 288 JmriJOptionPane.showMessageDialog(this, 289 Bundle.getMessage("NeedToBuildBeforeRunFile", 290 train.getName()), 291 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 292 } else { 293 // Add csv manifest file to our collection to be processed. 294 InstanceManager.getDefault(TrainCustomManifest.class).addCsvFile(train.createCsvManifestFile()); 295 train.setPrinted(true); 296 } 297 } 298 } 299 300 // Now run the user specified custom Manifest processor program 301 InstanceManager.getDefault(TrainCustomManifest.class).process(); 302 } 303 if (ae.getSource() == switchListsButton) { 304 trainScheduleManager.buildSwitchLists(); 305 } 306 if (ae.getSource() == terminateButton) { 307 trainManager.terminateSelectedTrains(getSortByList()); 308 } 309 if (ae.getSource() == activateButton) { 310 trainScheduleManager.setTrainScheduleActiveId(getSelectedScheduleId()); 311 activateButton.setEnabled(false); 312 } 313 if (ae.getSource() == saveButton) { 314 storeValues(); 315 if (Setup.isCloseWindowOnSaveEnabled()) { 316 dispose(); 317 } 318 } 319 } 320 321 /* 322 * Update radio button names in the same order as the table 323 */ 324 private void updateControlPanel() { 325 schedule.removeAll(); 326 noneButton.setName(TrainSchedule.NONE); // Name holds schedule id for the selected radio button 327 noneButton.setSelected(true); 328 commentTextArea.setText(""); // no text for the noneButton or anyButton 329 enableButtons(false); 330 schedule.add(noneButton); 331 schGroup.add(noneButton); 332 333 for (int i = trainsScheduleModel.getFixedColumn(); i < trainsScheduleModel.getColumnCount(); i++) { 334 log.debug("Column name: {}", trainsScheduleTable.getColumnName(i)); 335 TrainSchedule ts = trainScheduleManager.getScheduleByName(trainsScheduleTable.getColumnName(i)); 336 if (ts != null) { 337 JRadioButton b = new JRadioButton(); 338 b.setText(ts.getName()); 339 b.setName(ts.getId()); 340 schedule.add(b); 341 schGroup.add(b); 342 addRadioButtonAction(b); 343 if (b.getName().equals(trainScheduleManager.getTrainScheduleActiveId())) { 344 b.setSelected(true); 345 enableButtons(true); 346 // update comment field 347 commentTextArea.setText(ts.getComment()); 348 } 349 } 350 } 351 anyButton.setName(TrainSchedule.ANY); // Name holds schedule id for the selected radio button 352 schedule.add(anyButton); 353 schGroup.add(anyButton); 354 anyButton.setSelected(trainScheduleManager.getTrainScheduleActiveId().equals(TrainSchedule.ANY)); 355 schedule.revalidate(); 356 } 357 358 private void updateCheckboxes(boolean selected) { 359 TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId()); 360 if (ts != null) { 361 for (Train train : trainManager.getTrainsByIdList()) { 362 if (selected) { 363 ts.addTrainId(train.getId()); 364 } else { 365 ts.removeTrainId(train.getId()); 366 } 367 } 368 } 369 } 370 371 private void applySchedule() { 372 TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId()); 373 if (ts != null) { 374 for (Train train : trainManager.getTrainsByIdList()) { 375 train.setBuildEnabled(ts.containsTrainId(train.getId())); 376 } 377 } 378 } 379 380 private void copySchedule() { 381 TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId()); 382 if (ts != null) { 383 for (Train train : trainManager.getTrainsByIdList()) { 384 if (train.isBuildEnabled()) { 385 ts.addTrainId(train.getId()); 386 } else { 387 ts.removeTrainId(train.getId()); 388 } 389 } 390 } 391 } 392 393 private String getSelectedScheduleId() { 394 AbstractButton b; 395 Enumeration<AbstractButton> en = schGroup.getElements(); 396 while (en.hasMoreElements()) { 397 b = en.nextElement(); 398 if (b.isSelected()) { 399 log.debug("schedule radio button {}", b.getText()); 400 return b.getName(); 401 } 402 } 403 return null; 404 } 405 406 private void enableButtons(boolean enable) { 407 selectButton.setEnabled(enable); 408 copyButton.setEnabled(enable); 409 clearButton.setEnabled(enable); 410 applyButton.setEnabled(enable); 411 buildButton.setEnabled(enable); 412 printButton.setEnabled(enable); 413 runFileButton.setEnabled(enable); 414 switchListsButton.setEnabled(enable); 415 terminateButton.setEnabled(enable); 416 417 log.debug("Selected id: {}, Active id: {}", getSelectedScheduleId(), 418 trainScheduleManager.getTrainScheduleActiveId()); 419 420 activateButton.setEnabled(getSelectedScheduleId() != null && 421 !getSelectedScheduleId().equals(trainScheduleManager.getTrainScheduleActiveId())); 422 423 commentTextArea.setEnabled(enable); 424 } 425 426 private List<Train> getSortByList() { 427 if (sortByTime.isSelected()) { 428 return trainManager.getTrainsByTimeList(); 429 } else { 430 return trainManager.getTrainsByNameList(); 431 } 432 } 433 434 private void setSwitchListButtonText() { 435 if (!Setup.isSwitchListRealTime()) { 436 switchListsButton.setText(Bundle.getMessage("Update")); 437 } else if (trainManager.isPrintPreviewEnabled()) { 438 switchListsButton.setText(Bundle.getMessage("PreviewSwitchLists")); 439 } else { 440 switchListsButton.setText(Bundle.getMessage("PrintSwitchLists")); 441 } 442 } 443 444 // Modifies button text and tool tips 445 private void setPrintButtonText() { 446 if (trainManager.isPrintPreviewEnabled()) { 447 printButton.setText(Bundle.getMessage("Preview")); 448 printButton.setToolTipText(Bundle.getMessage("PreviewSelectedTip")); 449 } else { 450 printButton.setText(Bundle.getMessage("Print")); 451 printButton.setToolTipText(Bundle.getMessage("PrintSelectedTip")); 452 } 453 } 454 455 private void updateSwitchListButton() { 456 List<Location> locations = locationManager.getList(); 457 for (Location location : locations) { 458 if (location != null && location.isSwitchListEnabled() && location.getStatus().equals(Location.MODIFIED)) { 459 switchListsButton.setBackground(Color.RED); 460 return; 461 } 462 } 463 switchListsButton.setBackground(Color.GREEN); 464 } 465 466 private void updateRunButton() { 467 runFileButton.setVisible(Setup.isGenerateCsvManifestEnabled()); 468 } 469 470 @Override 471 protected void storeValues() { 472 // Save comment 473 TrainSchedule ts = trainScheduleManager.getScheduleById(getSelectedScheduleId()); 474 if (ts != null) { 475 ts.setComment(commentTextArea.getText()); 476 } 477 OperationsXml.save(); 478 } 479 480 @Override 481 public void dispose() { 482 Setup.getDefault().removePropertyChangeListener(this); 483 trainManager.removePropertyChangeListener(this); 484 trainScheduleManager.removePropertyChangeListener(this); 485 removePropertyChangeTrainSchedules(); 486 removePropertyChangeLocations(); 487 trainsScheduleModel.dispose(); 488 InstanceManager.getOptionalDefault(JTablePersistenceManager.class).ifPresent(tpm -> { 489 tpm.stopPersisting(trainsScheduleTable); 490 }); 491 super.dispose(); 492 } 493 494 private void addPropertyChangeLocations() { 495 for (Location location : locationManager.getList()) { 496 location.addPropertyChangeListener(this); 497 } 498 } 499 500 private void removePropertyChangeLocations() { 501 for (Location location : locationManager.getList()) { 502 location.removePropertyChangeListener(this); 503 } 504 } 505 506 private void addPropertyChangeTrainSchedules() { 507 List<TrainSchedule> trainSchedules = trainScheduleManager.getSchedulesByIdList(); 508 for (TrainSchedule ts : trainSchedules) { 509 ts.addPropertyChangeListener(this); 510 } 511 } 512 513 private void removePropertyChangeTrainSchedules() { 514 List<TrainSchedule> trainSchedules = trainScheduleManager.getSchedulesByIdList(); 515 for (TrainSchedule ts : trainSchedules) { 516 ts.removePropertyChangeListener(this); 517 } 518 } 519 520 @Override 521 public void propertyChange(PropertyChangeEvent e) { 522 if (Control.SHOW_PROPERTY) 523 log.debug("Property change {} old: {} new: {}", e.getPropertyName(), e.getOldValue(), e.getNewValue()); 524 if (e.getPropertyName().equals(TrainScheduleManager.LISTLENGTH_CHANGED_PROPERTY) || 525 e.getPropertyName().equals(TrainScheduleManager.SCHEDULE_ID_CHANGED_PROPERTY) || 526 e.getPropertyName().equals(TrainSchedule.NAME_CHANGED_PROPERTY)) { 527 updateControlPanel(); 528 } 529 if (e.getPropertyName().equals(TrainManager.PRINTPREVIEW_CHANGED_PROPERTY)) { 530 setPrintButtonText(); 531 setSwitchListButtonText(); 532 } 533 if (e.getPropertyName().equals(TrainManager.TRAINS_BUILT_CHANGED_PROPERTY)) { 534 switchListsButton.setEnabled(true); 535 runFileButton.setEnabled(true); 536 } 537 if (e.getPropertyName().equals(Setup.REAL_TIME_PROPERTY_CHANGE)) { 538 setSwitchListButtonText(); 539 } 540 if (e.getPropertyName().equals(Location.STATUS_CHANGED_PROPERTY) || 541 e.getPropertyName().equals(Location.SWITCHLIST_CHANGED_PROPERTY)) { 542 log.debug("update switch list button location ({})", e.getSource()); 543 updateSwitchListButton(); 544 } 545 if (e.getPropertyName().equals(Setup.MANIFEST_CSV_PROPERTY_CHANGE)) { 546 updateRunButton(); 547 } 548 } 549 550 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrainsScheduleTableFrame.class); 551}