001package jmri.jmrit.operations.trains; 002 003import java.awt.Color; 004import java.awt.Component; 005import java.awt.Frame; 006import java.beans.PropertyChangeEvent; 007import java.beans.PropertyChangeListener; 008import java.text.MessageFormat; 009import java.util.Hashtable; 010import java.util.List; 011 012import javax.swing.JButton; 013import javax.swing.JOptionPane; 014import javax.swing.JTable; 015import javax.swing.SwingUtilities; 016import javax.swing.table.DefaultTableCellRenderer; 017import javax.swing.table.TableCellEditor; 018 019import org.slf4j.Logger; 020import org.slf4j.LoggerFactory; 021 022import jmri.InstanceManager; 023import jmri.jmrit.beantable.EnablingCheckboxRenderer; 024import jmri.jmrit.operations.setup.Control; 025import jmri.jmrit.operations.setup.Setup; 026import jmri.util.swing.XTableColumnModel; 027import jmri.util.table.ButtonEditor; 028import jmri.util.table.ButtonRenderer; 029 030/** 031 * Table Model for edit of trains used by operations 032 * 033 * @author Daniel Boudreau Copyright (C) 2008, 2012 034 */ 035public class TrainsTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener { 036 037 TrainManager trainManager = InstanceManager.getDefault(TrainManager.class); // There is only one manager 038 volatile List<Train> sysList = trainManager.getTrainsByTimeList(); 039 JTable _table = null; 040 TrainsTableFrame _frame = null; 041 042 // Defines the columns 043 private static final int ID_COLUMN = 0; 044 private static final int TIME_COLUMN = ID_COLUMN + 1; 045 private static final int BUILDBOX_COLUMN = TIME_COLUMN + 1; 046 private static final int BUILD_COLUMN = BUILDBOX_COLUMN + 1; 047 private static final int NAME_COLUMN = BUILD_COLUMN + 1; 048 private static final int DESCRIPTION_COLUMN = NAME_COLUMN + 1; 049 private static final int BUILT_COLUMN = DESCRIPTION_COLUMN + 1; 050 private static final int CAR_ROAD_COLUMN = BUILT_COLUMN + 1; 051 private static final int LOCO_ROAD_COLUMN = CAR_ROAD_COLUMN + 1; 052 private static final int LOAD_COLUMN = LOCO_ROAD_COLUMN + 1; 053 private static final int OWNER_COLUMN = LOAD_COLUMN + 1; 054 private static final int ROUTE_COLUMN = OWNER_COLUMN + 1; 055 private static final int DEPARTS_COLUMN = ROUTE_COLUMN + 1; 056 private static final int TERMINATES_COLUMN = DEPARTS_COLUMN + 1; 057 private static final int CURRENT_COLUMN = TERMINATES_COLUMN + 1; 058 private static final int STATUS_COLUMN = CURRENT_COLUMN + 1; 059 private static final int ACTION_COLUMN = STATUS_COLUMN + 1; 060 private static final int EDIT_COLUMN = ACTION_COLUMN + 1; 061 062 private static final int HIGHESTCOLUMN = EDIT_COLUMN + 1; 063 064 public TrainsTableModel() { 065 super(); 066 trainManager.addPropertyChangeListener(this); 067 Setup.getDefault().addPropertyChangeListener(this); 068 updateList(); 069 } 070 071 public final int SORTBYTIME = 2; 072 public final int SORTBYID = 7; 073 074 private int _sort = SORTBYTIME; 075 076 public void setSort(int sort) { 077 _sort = sort; 078 updateList(); 079 updateColumnVisible(); 080 } 081 082 private boolean _showAll = true; 083 084 public void setShowAll(boolean showAll) { 085 _showAll = showAll; 086 updateList(); 087 fireTableDataChanged(); 088 } 089 090 public boolean isShowAll() { 091 return _showAll; 092 } 093 094 private void updateList() { 095 // first, remove listeners from the individual objects 096 removePropertyChangeTrains(); 097 098 List<Train> tempList; 099 if (_sort == SORTBYID) { 100 tempList = trainManager.getTrainsByIdList(); 101 } else { 102 tempList = trainManager.getTrainsByTimeList(); 103 } 104 105 if (!isShowAll()) { 106 // filter out trains not checked 107 for (int i = tempList.size() - 1; i >= 0; i--) { 108 if (!tempList.get(i).isBuildEnabled()) { 109 tempList.remove(i); 110 } 111 } 112 } 113 sysList = tempList; 114 115 // and add listeners back in 116 addPropertyChangeTrains(); 117 } 118 119 private Train getTrainByRow(int row) { 120 return sysList.get(row); 121 } 122 123 void initTable(JTable table, TrainsTableFrame frame) { 124 _table = table; 125 _frame = frame; 126 // allow row color to be controlled 127 table.setDefaultRenderer(Object.class, new MyTableCellRenderer()); 128 initTable(); 129 } 130 131 // Train frame table column widths, starts with id column and ends with edit 132 private final int[] _tableColumnWidths = {50, 50, 50, 72, 100, 140, 50, 50, 50, 50, 50, 120, 120, 120, 120, 120, 90, 133 70}; 134 135 void initTable() { 136 // Use XTableColumnModel so we can control which columns are visible 137 XTableColumnModel tcm = new XTableColumnModel(); 138 _table.setColumnModel(tcm); 139 _table.createDefaultColumnsFromModel(); 140 141 // Install the button handlers 142 ButtonRenderer buttonRenderer = new ButtonRenderer(); 143 ButtonRenderer buttonRenderer2 = new ButtonRenderer(); // for tool tips 144 TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton()); 145 tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer); 146 tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor); 147 tcm.getColumn(ACTION_COLUMN).setCellRenderer(buttonRenderer); 148 tcm.getColumn(ACTION_COLUMN).setCellEditor(buttonEditor); 149 tcm.getColumn(BUILD_COLUMN).setCellRenderer(buttonRenderer2); 150 tcm.getColumn(BUILD_COLUMN).setCellEditor(buttonEditor); 151 _table.setDefaultRenderer(Boolean.class, new EnablingCheckboxRenderer()); 152 153 // set column preferred widths 154 for (int i = 0; i < tcm.getColumnCount(); i++) { 155 tcm.getColumn(i).setPreferredWidth(_tableColumnWidths[i]); 156 } 157 _frame.loadTableDetails(_table); 158 159 // turn off column 160 updateColumnVisible(); 161 } 162 163 private void updateColumnVisible() { 164 XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel(); 165 tcm.setColumnVisible(tcm.getColumnByModelIndex(ID_COLUMN), _sort == SORTBYID); 166 tcm.setColumnVisible(tcm.getColumnByModelIndex(TIME_COLUMN), _sort == SORTBYTIME); 167 tcm.setColumnVisible(tcm.getColumnByModelIndex(BUILT_COLUMN), trainManager.isBuiltRestricted()); 168 tcm.setColumnVisible(tcm.getColumnByModelIndex(CAR_ROAD_COLUMN), trainManager.isCarRoadRestricted()); 169 tcm.setColumnVisible(tcm.getColumnByModelIndex(LOCO_ROAD_COLUMN), trainManager.isLocoRoadRestricted()); 170 tcm.setColumnVisible(tcm.getColumnByModelIndex(LOAD_COLUMN), trainManager.isLoadRestricted()); 171 tcm.setColumnVisible(tcm.getColumnByModelIndex(OWNER_COLUMN), trainManager.isOwnerRestricted()); 172 } 173 174 @Override 175 public int getRowCount() { 176 return sysList.size(); 177 } 178 179 @Override 180 public int getColumnCount() { 181 return HIGHESTCOLUMN; 182 } 183 184 public static final String IDCOLUMNNAME = Bundle.getMessage("Id"); 185 public static final String TIMECOLUMNNAME = Bundle.getMessage("Time"); 186 public static final String BUILDBOXCOLUMNNAME = Bundle.getMessage("Build"); 187 public static final String BUILDCOLUMNNAME = Bundle.getMessage("Function"); 188 public static final String NAMECOLUMNNAME = Bundle.getMessage("Name"); 189 public static final String DESCRIPTIONCOLUMNNAME = Bundle.getMessage("Description"); 190 public static final String ROUTECOLUMNNAME = Bundle.getMessage("Route"); 191 public static final String DEPARTSCOLUMNNAME = Bundle.getMessage("Departs"); 192 public static final String CURRENTCOLUMNNAME = Bundle.getMessage("Current"); 193 public static final String TERMINATESCOLUMNNAME = Bundle.getMessage("Terminates"); 194 public static final String STATUSCOLUMNNAME = Bundle.getMessage("Status"); 195 public static final String ACTIONCOLUMNNAME = Bundle.getMessage("Action"); 196 public static final String EDITCOLUMNNAME = Bundle.getMessage("ButtonEdit"); 197 198 @Override 199 public String getColumnName(int col) { 200 switch (col) { 201 case ID_COLUMN: 202 return IDCOLUMNNAME; 203 case TIME_COLUMN: 204 return TIMECOLUMNNAME; 205 case BUILDBOX_COLUMN: 206 return BUILDBOXCOLUMNNAME; 207 case BUILD_COLUMN: 208 return BUILDCOLUMNNAME; 209 case NAME_COLUMN: 210 return NAMECOLUMNNAME; 211 case DESCRIPTION_COLUMN: 212 return DESCRIPTIONCOLUMNNAME; 213 case BUILT_COLUMN: 214 return Bundle.getMessage("Built"); 215 case CAR_ROAD_COLUMN: 216 return Bundle.getMessage("RoadCar"); 217 case LOCO_ROAD_COLUMN: 218 return Bundle.getMessage("Road"); 219 case LOAD_COLUMN: 220 return Bundle.getMessage("Load"); 221 case OWNER_COLUMN: 222 return Bundle.getMessage("Owner"); 223 case ROUTE_COLUMN: 224 return ROUTECOLUMNNAME; 225 case DEPARTS_COLUMN: 226 return DEPARTSCOLUMNNAME; 227 case CURRENT_COLUMN: 228 return CURRENTCOLUMNNAME; 229 case TERMINATES_COLUMN: 230 return TERMINATESCOLUMNNAME; 231 case STATUS_COLUMN: 232 return STATUSCOLUMNNAME; 233 case ACTION_COLUMN: 234 return ACTIONCOLUMNNAME; 235 case EDIT_COLUMN: 236 return EDITCOLUMNNAME; 237 default: 238 return "unknown"; // NOI18N 239 } 240 } 241 242 @Override 243 public Class<?> getColumnClass(int col) { 244 switch (col) { 245 case BUILDBOX_COLUMN: 246 return Boolean.class; 247 case ID_COLUMN: 248 return Integer.class; 249 case TIME_COLUMN: 250 case NAME_COLUMN: 251 case DESCRIPTION_COLUMN: 252 case BUILT_COLUMN: 253 case CAR_ROAD_COLUMN: 254 case LOCO_ROAD_COLUMN: 255 case LOAD_COLUMN: 256 case OWNER_COLUMN: 257 case ROUTE_COLUMN: 258 case DEPARTS_COLUMN: 259 case CURRENT_COLUMN: 260 case TERMINATES_COLUMN: 261 case STATUS_COLUMN: 262 return String.class; 263 case BUILD_COLUMN: 264 case ACTION_COLUMN: 265 case EDIT_COLUMN: 266 return JButton.class; 267 default: 268 return null; 269 } 270 } 271 272 @Override 273 public boolean isCellEditable(int row, int col) { 274 switch (col) { 275 case BUILD_COLUMN: 276 case BUILDBOX_COLUMN: 277 case ACTION_COLUMN: 278 case EDIT_COLUMN: 279 return true; 280 default: 281 return false; 282 } 283 } 284 285 @Override 286 public Object getValueAt(int row, int col) { 287 if (row >= getRowCount()) { 288 return "ERROR row " + row; // NOI18N 289 } 290 Train train = getTrainByRow(row); 291 if (train == null) { 292 return "ERROR train unknown " + row; // NOI18N 293 } 294 switch (col) { 295 case ID_COLUMN: 296 return Integer.parseInt(train.getId()); 297 case TIME_COLUMN: 298 return train.getDepartureTime(); 299 case NAME_COLUMN: 300 return train.getIconName(); 301 case DESCRIPTION_COLUMN: 302 return train.getDescription(); 303 case BUILDBOX_COLUMN: 304 return Boolean.valueOf(train.isBuildEnabled()); 305 case BUILT_COLUMN: 306 return getBuiltString(train); 307 case CAR_ROAD_COLUMN: 308 return getModifiedString(train.getCarRoadNames().length, train.getCarRoadOption().equals(Train.ALL_ROADS), 309 train.getCarRoadOption().equals(Train.INCLUDE_ROADS)); 310 case LOCO_ROAD_COLUMN: 311 return getModifiedString(train.getLocoRoadNames().length, train.getLocoRoadOption().equals(Train.ALL_ROADS), 312 train.getLocoRoadOption().equals(Train.INCLUDE_ROADS)); 313 case LOAD_COLUMN: 314 return getModifiedString(train.getLoadNames().length, train.getLoadOption().equals(Train.ALL_LOADS), 315 train.getLoadOption().equals(Train.INCLUDE_LOADS)); 316 case OWNER_COLUMN: 317 return getModifiedString(train.getOwnerNames().length, train.getOwnerOption().equals(Train.ALL_OWNERS), 318 train.getOwnerOption().equals(Train.INCLUDE_OWNERS)); 319 case ROUTE_COLUMN: 320 return train.getTrainRouteName(); 321 case DEPARTS_COLUMN: { 322 if (train.getDepartureTrack() == null) { 323 return train.getTrainDepartsName(); 324 } else { 325 return train.getTrainDepartsName() + " (" + train.getDepartureTrack().getName() + ")"; 326 } 327 } 328 case CURRENT_COLUMN: 329 return train.getCurrentLocationName(); 330 case TERMINATES_COLUMN: { 331 if (train.getTerminationTrack() == null) { 332 return train.getTrainTerminatesName(); 333 } else { 334 return train.getTrainTerminatesName() + " (" + train.getTerminationTrack().getName() + ")"; 335 } 336 } 337 case STATUS_COLUMN: 338 return train.getStatus(); 339 case BUILD_COLUMN: { 340 if (train.isBuilt()) { 341 if (Setup.isGenerateCsvManifestEnabled() && trainManager.isOpenFileEnabled()) { 342 setToolTip(MessageFormat.format(Bundle.getMessage("OpenTrainTip"), 343 new Object[] { train.getName() }), row, col); 344 return Bundle.getMessage("OpenFile"); 345 } 346 if (Setup.isGenerateCsvManifestEnabled() && trainManager.isRunFileEnabled()) { 347 setToolTip(MessageFormat.format(Bundle.getMessage("RunTrainTip"), 348 new Object[] { train.getName() }), row, col); 349 return Bundle.getMessage("RunFile"); 350 } 351 setToolTip(Bundle.getMessage("PrintTrainTip"), row, col); 352 if (trainManager.isPrintPreviewEnabled()) { 353 return Bundle.getMessage("Preview"); 354 } else if (train.isPrinted()) { 355 return Bundle.getMessage("Printed"); 356 } else { 357 return Bundle.getMessage("Print"); 358 } 359 } 360 setToolTip(MessageFormat.format(Bundle.getMessage("BuildTrainTip"), new Object[] { train.getName() }), 361 row, col); 362 return Bundle.getMessage("Build"); 363 } 364 case ACTION_COLUMN: { 365 if (train.isBuildFailed()) { 366 return Bundle.getMessage("Report"); 367 } 368 if (train.getCurrentRouteLocation() == train.getTrainTerminatesRouteLocation() && 369 trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.MOVE)) { 370 return Bundle.getMessage("Terminate"); 371 } 372 return trainManager.getTrainsFrameTrainAction(); 373 } 374 case EDIT_COLUMN: 375 return Bundle.getMessage("ButtonEdit"); 376 default: 377 return "unknown " + col; // NOI18N 378 } 379 } 380 381 private void setToolTip(String text, int row, int col) { 382 XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel(); 383 ButtonRenderer buttonRenderer = (ButtonRenderer) tcm.getColumnByModelIndex(col).getCellRenderer(); 384 if (buttonRenderer != null) { 385 buttonRenderer.setToolTipText(text); 386 } 387 } 388 389 private String getBuiltString(Train train) { 390 if (!train.getBuiltStartYear().equals(Train.NONE) && train.getBuiltEndYear().equals(Train.NONE)) { 391 return "A " + train.getBuiltStartYear(); 392 } 393 if (train.getBuiltStartYear().equals(Train.NONE) && !train.getBuiltEndYear().equals(Train.NONE)) { 394 return "B " + train.getBuiltEndYear(); 395 } 396 if (!train.getBuiltStartYear().equals(Train.NONE) && !train.getBuiltEndYear().equals(Train.NONE)) { 397 return "R " + train.getBuiltStartYear() + ":" + train.getBuiltEndYear(); 398 } 399 return ""; 400 } 401 402 private String getModifiedString(int number, boolean all, boolean accept) { 403 if (all) { 404 return ""; 405 } 406 if (accept) { 407 return "A " + Integer.toString(number); // NOI18N 408 } 409 return "E " + Integer.toString(number); // NOI18N 410 } 411 412 @Override 413 public void setValueAt(Object value, int row, int col) { 414 switch (col) { 415 case EDIT_COLUMN: 416 editTrain(row); 417 break; 418 case BUILD_COLUMN: 419 buildTrain(row); 420 break; 421 case ACTION_COLUMN: 422 actionTrain(row); 423 break; 424 case BUILDBOX_COLUMN: { 425 Train train = getTrainByRow(row); 426 train.setBuildEnabled(((Boolean) value).booleanValue()); 427 break; 428 } 429 default: 430 break; 431 } 432 } 433 434 public Color getRowColor(int row) { 435 Train train = getTrainByRow(row); 436 return train.getTableRowColor(); 437 } 438 439 TrainEditFrame tef = null; 440 441 private void editTrain(int row) { 442 if (tef != null) { 443 tef.dispose(); 444 } 445 // use invokeLater so new window appears on top 446 SwingUtilities.invokeLater(() -> { 447 Train train = getTrainByRow(row); 448 log.debug("Edit train ({})", train.getName()); 449 tef = new TrainEditFrame(train); 450 }); 451 } 452 453 Thread build; 454 455 private void buildTrain(int row) { 456 final Train train = getTrainByRow(row); 457 if (!train.isBuilt()) { 458 // only one train build at a time 459 if (build != null && build.isAlive()) { 460 return; 461 } 462 // use a thread to allow table updates during build 463 build = jmri.util.ThreadingUtil.newThread(new Runnable() { 464 @Override 465 public void run() { 466 train.build(); 467 } 468 }); 469 build.setName("Build Train (" + train.getName() + ")"); // NOI18N 470 build.start(); 471 // print build report, print manifest, run or open file 472 } else { 473 if (trainManager.isBuildReportEnabled()) { 474 train.printBuildReport(); 475 } 476 if (Setup.isGenerateCsvManifestEnabled() && trainManager.isOpenFileEnabled()) { 477 train.openFile(); 478 } else if (Setup.isGenerateCsvManifestEnabled() && trainManager.isRunFileEnabled()) { 479 train.runFile(); 480 } else { 481 if (!train.printManifestIfBuilt()) { 482 log.debug("Manifest file for train ({}) not found", train.getName()); 483 int result = JOptionPane.showConfirmDialog(null, 484 MessageFormat.format(Bundle.getMessage("TrainManifestFileMissing"), 485 new Object[] { train.getName() }), 486 Bundle.getMessage("TrainManifestFileError"), JOptionPane.YES_NO_OPTION); 487 if (result == JOptionPane.YES_OPTION) { 488 train.setModified(true); 489 if (!train.printManifestIfBuilt()) { 490 log.error("Not able to create manifest for train ({})", train.getName()); 491 } 492 } 493 } 494 } 495 } 496 } 497 498 // one of five buttons: Report, Move, Reset, Conductor or Terminate 499 private void actionTrain(int row) { 500 // no actions while a train is being built 501 if (build != null && build.isAlive()) { 502 return; 503 } 504 Train train = getTrainByRow(row); 505 // move button becomes report if failure 506 if (train.isBuildFailed()) { 507 train.printBuildReport(); 508 } else if (trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.RESET)) { 509 log.debug("Reset train ({})", train.getName()); 510 // check to see if departure track was reused 511 if (checkDepartureTrack(train)) { 512 log.debug("Train is departing staging that already has inbound cars"); 513 JOptionPane.showMessageDialog(null, 514 MessageFormat.format(Bundle.getMessage("StagingTrackUsed"), 515 new Object[] { train.getDepartureTrack().getName() }), 516 Bundle.getMessage("CanNotResetTrain"), JOptionPane.INFORMATION_MESSAGE); 517 } else if (!train.reset()) { 518 JOptionPane.showMessageDialog(null, 519 MessageFormat.format(Bundle.getMessage("TrainIsInRoute"), 520 new Object[] { train.getTrainTerminatesName() }), 521 Bundle.getMessage("CanNotResetTrain"), JOptionPane.ERROR_MESSAGE); 522 } 523 } else if (!train.isBuilt()) { 524 JOptionPane.showMessageDialog(null, 525 MessageFormat.format(Bundle.getMessage("TrainNeedsBuild"), new Object[] { train.getName() }), 526 Bundle.getMessage("CanNotPerformAction"), JOptionPane.INFORMATION_MESSAGE); 527 } else if (train.isBuilt() && trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.MOVE)) { 528 log.debug("Move train ({})", train.getName()); 529 train.move(); 530 } else if (train.isBuilt() && trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.TERMINATE)) { 531 log.debug("Terminate train ({})", train.getName()); 532 int status = JOptionPane.showConfirmDialog(null, 533 MessageFormat.format(Bundle.getMessage("TerminateTrain"), 534 new Object[] { train.getName(), train.getDescription() }), 535 MessageFormat.format(Bundle.getMessage("DoYouWantToTermiate"), new Object[] { train.getName() }), 536 JOptionPane.YES_NO_OPTION); 537 if (status == JOptionPane.YES_OPTION) { 538 train.terminate(); 539 } 540 } else if (train.isBuilt() && trainManager.getTrainsFrameTrainAction().equals(TrainsTableFrame.CONDUCTOR)) { 541 log.debug("Enable conductor for train ({})", train.getName()); 542 launchConductor(train); 543 } 544 } 545 546 /* 547 * Check to see if the departure track in staging has been taken by another 548 * train. return true if track has been allocated to another train. 549 */ 550 private boolean checkDepartureTrack(Train train) { 551 return (Setup.isStagingTrackImmediatelyAvail() && 552 !train.isTrainEnRoute() && 553 train.getDepartureTrack() != null && 554 train.getDepartureTrack().isStaging() && 555 train.getDepartureTrack() != train.getTerminationTrack() && 556 train.getDepartureTrack().getDropRS() > 0); 557 } 558 559 private static Hashtable<String, TrainConductorFrame> _trainConductorHashTable = new Hashtable<>(); 560 561 private void launchConductor(Train train) { 562 // use invokeLater so new window appears on top 563 SwingUtilities.invokeLater(() -> { 564 TrainConductorFrame f = _trainConductorHashTable.get(train.getId()); 565 // create a copy train frame 566 if (f == null || !f.isVisible()) { 567 f = new TrainConductorFrame(train); 568 _trainConductorHashTable.put(train.getId(), f); 569 } else { 570 f.setExtendedState(Frame.NORMAL); 571 } 572 f.setVisible(true); // this also brings the frame into focus 573 }); 574 } 575 576 @Override 577 public void propertyChange(PropertyChangeEvent e) { 578 if (Control.SHOW_PROPERTY) { 579 log.debug("Property change {} old: {} new: {}", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N 580 } 581 if (e.getPropertyName().equals(Train.BUILT_YEAR_CHANGED_PROPERTY) || 582 e.getPropertyName().equals(Train.ROADS_CHANGED_PROPERTY) || 583 e.getPropertyName().equals(Train.LOADS_CHANGED_PROPERTY) || 584 e.getPropertyName().equals(Train.OWNERS_CHANGED_PROPERTY)) { 585 updateColumnVisible(); 586 } 587 if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY) || 588 e.getPropertyName().equals(TrainManager.PRINTPREVIEW_CHANGED_PROPERTY) || 589 e.getPropertyName().equals(TrainManager.OPEN_FILE_CHANGED_PROPERTY) || 590 e.getPropertyName().equals(TrainManager.RUN_FILE_CHANGED_PROPERTY) || 591 e.getPropertyName().equals(Setup.MANIFEST_CSV_PROPERTY_CHANGE) || 592 e.getPropertyName().equals(TrainManager.TRAIN_ACTION_CHANGED_PROPERTY) || 593 e.getPropertyName().equals(Train.DEPARTURETIME_CHANGED_PROPERTY) || 594 (e.getPropertyName().equals(Train.BUILD_CHANGED_PROPERTY) && !isShowAll())) { 595 SwingUtilities.invokeLater(() -> { 596 updateList(); 597 fireTableDataChanged(); 598 }); 599 } else if (e.getSource().getClass().equals(Train.class)) { 600 Train train = ((Train) e.getSource()); 601 SwingUtilities.invokeLater(() -> { 602 int row = sysList.indexOf(train); 603 if (row >= 0 && _table != null) { 604 fireTableRowsUpdated(row, row); 605 int viewRow = _table.convertRowIndexToView(row); 606 _table.scrollRectToVisible(_table.getCellRect(viewRow, 0, true)); 607 } 608 }); 609 } 610 } 611 612 private void removePropertyChangeTrains() { 613 for (Train train : trainManager.getTrainsByIdList()) { 614 train.removePropertyChangeListener(this); 615 } 616 } 617 618 private void addPropertyChangeTrains() { 619 for (Train train : trainManager.getTrainsByIdList()) { 620 train.addPropertyChangeListener(this); 621 } 622 } 623 624 public void dispose() { 625 if (tef != null) { 626 tef.dispose(); 627 } 628 trainManager.removePropertyChangeListener(this); 629 Setup.getDefault().removePropertyChangeListener(this); 630 removePropertyChangeTrains(); 631 } 632 633 class MyTableCellRenderer extends DefaultTableCellRenderer { 634 635 @Override 636 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, 637 int row, int column) { 638 Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 639 if (!isSelected) { 640 int modelRow = table.convertRowIndexToModel(row); 641 // log.debug("View row: {} Column: {} Model row: {}", row, column, modelRow); 642 Color background = getRowColor(modelRow); 643 component.setBackground(background); 644 component.setForeground(getForegroundColor(background)); 645 } 646 return component; 647 } 648 649 Color[] darkColors = { Color.BLACK, Color.BLUE, Color.GRAY, Color.RED, Color.MAGENTA }; 650 651 /** 652 * Dark colors need white lettering 653 * 654 */ 655 private Color getForegroundColor(Color background) { 656 if (background == null) { 657 return null; 658 } 659 for (Color color : darkColors) { 660 if (background == color) { 661 return Color.WHITE; 662 } 663 } 664 return Color.BLACK; // all others get black lettering 665 } 666 } 667 668 private final static Logger log = LoggerFactory.getLogger(TrainsTableModel.class); 669}