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