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