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