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