001package jmri.jmrit.operations.locations.schedules; 002 003import java.awt.Color; 004import java.beans.PropertyChangeEvent; 005import java.beans.PropertyChangeListener; 006import java.text.MessageFormat; 007import java.util.ArrayList; 008import java.util.List; 009 010import javax.swing.*; 011import javax.swing.table.TableCellEditor; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016import jmri.InstanceManager; 017import jmri.jmrit.operations.OperationsTableModel; 018import jmri.jmrit.operations.locations.*; 019import jmri.jmrit.operations.rollingstock.cars.*; 020import jmri.jmrit.operations.setup.Control; 021import jmri.jmrit.operations.trains.schedules.TrainSchedule; 022import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 023import jmri.util.swing.XTableColumnModel; 024import jmri.util.table.ButtonEditor; 025import jmri.util.table.ButtonRenderer; 026 027/** 028 * Table Model for edit of a schedule used by operations 029 * 030 * @author Daniel Boudreau Copyright (C) 2009, 2014 031 */ 032public class ScheduleTableModel extends OperationsTableModel implements PropertyChangeListener { 033 034 protected static final String POINTER = " -->"; 035 036 // Defines the columns 037 private static final int ID_COLUMN = 0; 038 private static final int CURRENT_COLUMN = ID_COLUMN + 1; 039 private static final int TYPE_COLUMN = CURRENT_COLUMN + 1; 040 private static final int RANDOM_COLUMN = TYPE_COLUMN + 1; 041 private static final int SETOUT_DAY_COLUMN = RANDOM_COLUMN + 1; 042 private static final int ROAD_COLUMN = SETOUT_DAY_COLUMN + 1; 043 private static final int LOAD_COLUMN = ROAD_COLUMN + 1; 044 private static final int SHIP_COLUMN = LOAD_COLUMN + 1; 045 private static final int DEST_COLUMN = SHIP_COLUMN + 1; 046 private static final int TRACK_COLUMN = DEST_COLUMN + 1; 047 private static final int PICKUP_DAY_COLUMN = TRACK_COLUMN + 1; 048 private static final int COUNT_COLUMN = PICKUP_DAY_COLUMN + 1; 049 private static final int HIT_COLUMN = COUNT_COLUMN + 1; 050 private static final int WAIT_COLUMN = HIT_COLUMN + 1; 051 private static final int UP_COLUMN = WAIT_COLUMN + 1; 052 private static final int DOWN_COLUMN = UP_COLUMN + 1; 053 private static final int DELETE_COLUMN = DOWN_COLUMN + 1; 054 055 private static final int HIGHEST_COLUMN = DELETE_COLUMN + 1; 056 057 public ScheduleTableModel() { 058 super(); 059 } 060 061 Schedule _schedule; 062 Location _location; 063 Track _track; 064 JTable _table; 065 ScheduleEditFrame _frame; 066 boolean _matchMode = false; 067 068 private void updateList() { 069 if (_schedule == null) { 070 return; 071 } 072 // first, remove listeners from the individual objects 073 removePropertyChangeScheduleItems(); 074 _list = _schedule.getItemsBySequenceList(); 075 // and add them back in 076 for (ScheduleItem si : _list) { 077 si.addPropertyChangeListener(this); 078 // TODO the following two property changes could be moved to ScheduleItem 079 // covers the cases where destination or track is deleted 080 if (si.getDestination() != null) { 081 si.getDestination().addPropertyChangeListener(this); 082 } 083 if (si.getDestinationTrack() != null) { 084 si.getDestinationTrack().addPropertyChangeListener(this); 085 } 086 } 087 } 088 089 List<ScheduleItem> _list = new ArrayList<>(); 090 091 protected void initTable(ScheduleEditFrame frame, JTable table, Schedule schedule, Location location, Track track) { 092 super.initTable(table); 093 _schedule = schedule; 094 _location = location; 095 _track = track; 096 _table = table; 097 _frame = frame; 098 099 // add property listeners 100 if (_schedule != null) { 101 _schedule.addPropertyChangeListener(this); 102 } 103 _location.addPropertyChangeListener(this); 104 _track.addPropertyChangeListener(this); 105 initTable(); 106 } 107 108 private void initTable() { 109 // Use XTableColumnModel so we can control which columns are visible 110 XTableColumnModel tcm = new XTableColumnModel(); 111 _table.setColumnModel(tcm); 112 _table.createDefaultColumnsFromModel(); 113 114 // Install the button handlers 115 ButtonRenderer buttonRenderer = new ButtonRenderer(); 116 TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton()); 117 tcm.getColumn(UP_COLUMN).setCellRenderer(buttonRenderer); 118 tcm.getColumn(UP_COLUMN).setCellEditor(buttonEditor); 119 tcm.getColumn(DOWN_COLUMN).setCellRenderer(buttonRenderer); 120 tcm.getColumn(DOWN_COLUMN).setCellEditor(buttonEditor); 121 tcm.getColumn(DELETE_COLUMN).setCellRenderer(buttonRenderer); 122 tcm.getColumn(DELETE_COLUMN).setCellEditor(buttonEditor); 123 124 // set column preferred widths 125 _table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(35); 126 _table.getColumnModel().getColumn(CURRENT_COLUMN).setPreferredWidth(50); 127 _table.getColumnModel().getColumn(TYPE_COLUMN).setPreferredWidth(90); 128 _table.getColumnModel().getColumn(RANDOM_COLUMN).setPreferredWidth(60); 129 _table.getColumnModel().getColumn(SETOUT_DAY_COLUMN).setPreferredWidth(90); 130 _table.getColumnModel().getColumn(ROAD_COLUMN).setPreferredWidth(90); 131 _table.getColumnModel().getColumn(LOAD_COLUMN).setPreferredWidth(90); 132 _table.getColumnModel().getColumn(SHIP_COLUMN).setPreferredWidth(90); 133 _table.getColumnModel().getColumn(DEST_COLUMN).setPreferredWidth(130); 134 _table.getColumnModel().getColumn(TRACK_COLUMN).setPreferredWidth(130); 135 _table.getColumnModel().getColumn(PICKUP_DAY_COLUMN).setPreferredWidth(90); 136 _table.getColumnModel().getColumn(COUNT_COLUMN).setPreferredWidth(45); 137 _table.getColumnModel().getColumn(HIT_COLUMN).setPreferredWidth(45); 138 _table.getColumnModel().getColumn(WAIT_COLUMN).setPreferredWidth(40); 139 _table.getColumnModel().getColumn(UP_COLUMN).setPreferredWidth(60); 140 _table.getColumnModel().getColumn(DOWN_COLUMN).setPreferredWidth(70); 141 _table.getColumnModel().getColumn(DELETE_COLUMN).setPreferredWidth(70); 142 143 _frame.loadTableDetails(_table); 144 // setup columns 145 tcm.setColumnVisible(tcm.getColumnByModelIndex(HIT_COLUMN), _matchMode); 146 tcm.setColumnVisible(tcm.getColumnByModelIndex(COUNT_COLUMN), !_matchMode); 147 148 // does not use a table sorter 149 _table.setRowSorter(null); 150 151 updateList(); 152 } 153 154 @Override 155 public int getRowCount() { 156 return _list.size(); 157 } 158 159 @Override 160 public int getColumnCount() { 161 return HIGHEST_COLUMN; 162 } 163 164 @Override 165 public String getColumnName(int col) { 166 switch (col) { 167 case ID_COLUMN: 168 return Bundle.getMessage("Id"); 169 case CURRENT_COLUMN: 170 return Bundle.getMessage("Current"); 171 case TYPE_COLUMN: 172 return Bundle.getMessage("Type"); 173 case RANDOM_COLUMN: 174 return Bundle.getMessage("Random"); 175 case SETOUT_DAY_COLUMN: 176 return Bundle.getMessage("Delivery"); 177 case ROAD_COLUMN: 178 return Bundle.getMessage("Road"); 179 case LOAD_COLUMN: 180 return Bundle.getMessage("Receive"); 181 case SHIP_COLUMN: 182 return Bundle.getMessage("Ship"); 183 case DEST_COLUMN: 184 return Bundle.getMessage("Destination"); 185 case TRACK_COLUMN: 186 return Bundle.getMessage("Track"); 187 case PICKUP_DAY_COLUMN: 188 return Bundle.getMessage("Pickup"); 189 case COUNT_COLUMN: 190 return Bundle.getMessage("Count"); 191 case HIT_COLUMN: 192 return Bundle.getMessage("Hits"); 193 case WAIT_COLUMN: 194 return Bundle.getMessage("Wait"); 195 case UP_COLUMN: 196 return Bundle.getMessage("Up"); 197 case DOWN_COLUMN: 198 return Bundle.getMessage("Down"); 199 case DELETE_COLUMN: 200 return Bundle.getMessage("ButtonDelete"); 201 default: 202 return "unknown"; // NOI18N 203 } 204 } 205 206 @Override 207 public Class<?> getColumnClass(int col) { 208 switch (col) { 209 case ID_COLUMN: 210 case CURRENT_COLUMN: 211 case TYPE_COLUMN: 212 return String.class; 213 case RANDOM_COLUMN: 214 case SETOUT_DAY_COLUMN: 215 case ROAD_COLUMN: 216 case LOAD_COLUMN: 217 case SHIP_COLUMN: 218 case DEST_COLUMN: 219 case TRACK_COLUMN: 220 case PICKUP_DAY_COLUMN: 221 return JComboBox.class; 222 case COUNT_COLUMN: 223 case HIT_COLUMN: 224 case WAIT_COLUMN: 225 return Integer.class; 226 case UP_COLUMN: 227 case DOWN_COLUMN: 228 case DELETE_COLUMN: 229 return JButton.class; 230 default: 231 return null; 232 } 233 } 234 235 @Override 236 public boolean isCellEditable(int row, int col) { 237 switch (col) { 238 case CURRENT_COLUMN: 239 case RANDOM_COLUMN: 240 case SETOUT_DAY_COLUMN: 241 case ROAD_COLUMN: 242 case LOAD_COLUMN: 243 case DEST_COLUMN: 244 case TRACK_COLUMN: 245 case PICKUP_DAY_COLUMN: 246 case COUNT_COLUMN: 247 case HIT_COLUMN: 248 case WAIT_COLUMN: 249 case UP_COLUMN: 250 case DOWN_COLUMN: 251 case DELETE_COLUMN: 252 return true; 253 case SHIP_COLUMN: 254 return !_track.isDisableLoadChangeEnabled(); 255 default: 256 return false; 257 } 258 } 259 260 @Override 261 public Object getValueAt(int row, int col) { 262 if (row >= getRowCount()) { 263 return "ERROR row " + row; // NOI18N 264 } 265 ScheduleItem si = _list.get(row); 266 if (si == null) { 267 return "ERROR schedule item unknown " + row; // NOI18N 268 } 269 switch (col) { 270 case ID_COLUMN: 271 return si.getId(); 272 case CURRENT_COLUMN: 273 return getCurrentPointer(si); 274 case TYPE_COLUMN: 275 return getType(si); 276 case RANDOM_COLUMN: 277 return getRandomComboBox(si); 278 case SETOUT_DAY_COLUMN: 279 return getSetoutDayComboBox(si); 280 case ROAD_COLUMN: 281 return getRoadComboBox(si); 282 case LOAD_COLUMN: 283 return getLoadComboBox(si); 284 case SHIP_COLUMN: 285 return getShipComboBox(si); 286 case DEST_COLUMN: 287 return getDestComboBox(si); 288 case TRACK_COLUMN: 289 return getTrackComboBox(si); 290 case PICKUP_DAY_COLUMN: 291 return getPickupDayComboBox(si); 292 case COUNT_COLUMN: 293 return si.getCount(); 294 case HIT_COLUMN: 295 return si.getHits(); 296 case WAIT_COLUMN: 297 return si.getWait(); 298 case UP_COLUMN: 299 return Bundle.getMessage("Up"); 300 case DOWN_COLUMN: 301 return Bundle.getMessage("Down"); 302 case DELETE_COLUMN: 303 return Bundle.getMessage("ButtonDelete"); 304 default: 305 return "unknown " + col; // NOI18N 306 } 307 } 308 309 @Override 310 public void setValueAt(Object value, int row, int col) { 311 if (value == null) { 312 log.debug("Warning schedule table row {} still in edit", row); 313 return; 314 } 315 switch (col) { 316 case CURRENT_COLUMN: 317 setCurrent(row); 318 break; 319 case RANDOM_COLUMN: 320 setRandom(value, row); 321 break; 322 case SETOUT_DAY_COLUMN: 323 setSetoutDay(value, row); 324 break; 325 case ROAD_COLUMN: 326 setRoad(value, row); 327 break; 328 case LOAD_COLUMN: 329 setLoad(value, row); 330 break; 331 case SHIP_COLUMN: 332 setShip(value, row); 333 break; 334 case DEST_COLUMN: 335 setDestination(value, row); 336 break; 337 case TRACK_COLUMN: 338 setTrack(value, row); 339 break; 340 case PICKUP_DAY_COLUMN: 341 setPickupDay(value, row); 342 break; 343 case COUNT_COLUMN: 344 setCount(value, row); 345 break; 346 case HIT_COLUMN: 347 setHit(value, row); 348 break; 349 case WAIT_COLUMN: 350 setWait(value, row); 351 break; 352 case UP_COLUMN: 353 moveUpScheduleItem(row); 354 break; 355 case DOWN_COLUMN: 356 moveDownScheduleItem(row); 357 break; 358 case DELETE_COLUMN: 359 deleteScheduleItem(row); 360 break; 361 default: 362 break; 363 } 364 } 365 366 @Override 367 protected Color getForegroundColor(int row) { 368 ScheduleItem si = _list.get(row); 369 if (!_schedule.checkScheduleItemValid(si, _track).equals(Schedule.SCHEDULE_OKAY)) { 370 return Color.red; 371 } 372 return super.getForegroundColor(row); 373 } 374 375 private String getCurrentPointer(ScheduleItem si) { 376 if (_track.getCurrentScheduleItem() == si) { 377 if (_track.getScheduleMode() == Track.SEQUENTIAL && si.getCount() > 1) { 378 return " " + _track.getScheduleCount() + POINTER; // NOI18N 379 } else { 380 return POINTER; // NOI18N 381 } 382 } else { 383 return ""; 384 } 385 } 386 387 private String getType(ScheduleItem si) { 388 if (_track.isTypeNameAccepted(si.getTypeName())) { 389 return si.getTypeName(); 390 } else { 391 return Bundle.getMessage("NotValid", si.getTypeName()); 392 } 393 } 394 395 private JComboBox<String> getRoadComboBox(ScheduleItem si) { 396 // log.debug("getRoadComboBox for ScheduleItem "+si.getType()); 397 JComboBox<String> cb = new JComboBox<>(); 398 cb.addItem(ScheduleItem.NONE); 399 for (String roadName : InstanceManager.getDefault(CarRoads.class).getNames()) { 400 if (_track.isRoadNameAccepted(roadName) && 401 InstanceManager.getDefault(CarManager.class).getByTypeAndRoad(si.getTypeName(), roadName) != null) { 402 cb.addItem(roadName); 403 } 404 } 405 cb.setSelectedItem(si.getRoadName()); 406 if (!cb.getSelectedItem().equals(si.getRoadName())) { 407 String notValid = Bundle.getMessage("NotValid", si.getRoadName()); 408 cb.addItem(notValid); 409 cb.setSelectedItem(notValid); 410 } 411 return cb; 412 } 413 414 String[] randomValues = {ScheduleItem.NONE, "50", "30", "25", "20", "15", "10", "5", "2", "1"}; // NOI18N 415 416 protected JComboBox<String> getRandomComboBox(ScheduleItem si) { 417 JComboBox<String> cb = new JComboBox<>(); 418 for (String item : randomValues) { 419 cb.addItem(item); 420 } 421 cb.setSelectedItem(si.getRandom()); 422 return cb; 423 } 424 425 private JComboBox<TrainSchedule> getSetoutDayComboBox(ScheduleItem si) { 426 JComboBox<TrainSchedule> cb = InstanceManager.getDefault(TrainScheduleManager.class).getSelectComboBox(); 427 TrainSchedule sch = 428 InstanceManager.getDefault(TrainScheduleManager.class).getScheduleById(si.getSetoutTrainScheduleId()); 429 if (sch != null) { 430 cb.setSelectedItem(sch); 431 } else if (!si.getSetoutTrainScheduleId().equals(ScheduleItem.NONE)) { 432 // error user deleted this set out day 433 String notValid = Bundle.getMessage("NotValid", si.getSetoutTrainScheduleId()); 434 TrainSchedule errorSchedule = new TrainSchedule(si.getSetoutTrainScheduleId(), notValid); 435 cb.addItem(errorSchedule); 436 cb.setSelectedItem(errorSchedule); 437 } 438 return cb; 439 } 440 441 private JComboBox<TrainSchedule> getPickupDayComboBox(ScheduleItem si) { 442 JComboBox<TrainSchedule> cb = InstanceManager.getDefault(TrainScheduleManager.class).getSelectComboBox(); 443 TrainSchedule sch = 444 InstanceManager.getDefault(TrainScheduleManager.class).getScheduleById(si.getPickupTrainScheduleId()); 445 if (sch != null) { 446 cb.setSelectedItem(sch); 447 } else if (!si.getPickupTrainScheduleId().equals(ScheduleItem.NONE)) { 448 // error user deleted this pick up day 449 String notValid = Bundle.getMessage("NotValid", si.getPickupTrainScheduleId()); 450 TrainSchedule errorSchedule = new TrainSchedule(si.getSetoutTrainScheduleId(), notValid); 451 cb.addItem(errorSchedule); 452 cb.setSelectedItem(errorSchedule); 453 } 454 return cb; 455 } 456 457 protected JComboBox<String> getLoadComboBox(ScheduleItem si) { 458 // log.debug("getLoadComboBox for ScheduleItem "+si.getType()); 459 JComboBox<String> cb = InstanceManager.getDefault(CarLoads.class).getSelectComboBox(si.getTypeName()); 460 filterLoads(si, cb); // remove loads not accepted by this track 461 cb.setSelectedItem(si.getReceiveLoadName()); 462 if (!cb.getSelectedItem().equals(si.getReceiveLoadName())) { 463 String notValid = Bundle.getMessage("NotValid", si.getReceiveLoadName()); 464 cb.addItem(notValid); 465 cb.setSelectedItem(notValid); 466 } 467 return cb; 468 } 469 470 protected JComboBox<String> getShipComboBox(ScheduleItem si) { 471 // log.debug("getShipComboBox for ScheduleItem "+si.getType()); 472 JComboBox<String> cb = InstanceManager.getDefault(CarLoads.class).getSelectComboBox(si.getTypeName()); 473 // if load change disabled, return receive load name 474 if (_track.isDisableLoadChangeEnabled()) { 475 cb.setSelectedItem(si.getReceiveLoadName()); 476 } else { 477 cb.setSelectedItem(si.getShipLoadName()); 478 if (!cb.getSelectedItem().equals(si.getShipLoadName())) { 479 String notValid = MessageFormat 480 .format(Bundle.getMessage("NotValid"), new Object[]{si.getShipLoadName()}); 481 cb.addItem(notValid); 482 cb.setSelectedItem(notValid); 483 } 484 } 485 return cb; 486 } 487 488 protected JComboBox<Location> getDestComboBox(ScheduleItem si) { 489 // log.debug("getDestComboBox for ScheduleItem "+si.getType()); 490 JComboBox<Location> cb = InstanceManager.getDefault(LocationManager.class).getComboBox(); 491 filterDestinations(cb, si.getTypeName()); 492 cb.setSelectedItem(si.getDestination()); 493 if (si.getDestination() != null && cb.getSelectedIndex() == -1) { 494 // user deleted destination, this is self correcting, when user restarts program, destination 495 // assignment will be gone. 496 cb.addItem(si.getDestination()); 497 cb.setSelectedItem(si.getDestination()); 498 } 499 return cb; 500 } 501 502 protected JComboBox<Track> getTrackComboBox(ScheduleItem si) { 503 // log.debug("getTrackComboBox for ScheduleItem "+si.getType()); 504 JComboBox<Track> cb = new JComboBox<>(); 505 if (si.getDestination() != null) { 506 Location dest = si.getDestination(); 507 dest.updateComboBox(cb); 508 filterTracks(dest, cb, si.getTypeName(), si.getRoadName(), si.getShipLoadName()); 509 cb.setSelectedItem(si.getDestinationTrack()); 510 if (si.getDestinationTrack() != null && cb.getSelectedIndex() == -1) { 511 // user deleted track at destination, this is self correcting, when user restarts program, track 512 // assignment will be gone. 513 cb.addItem(si.getDestinationTrack()); 514 cb.setSelectedItem(si.getDestinationTrack()); 515 } 516 } 517 return cb; 518 } 519 520 private void setCurrent(int row) { 521 ScheduleItem si = _list.get(row); 522 _track.setScheduleItemId(si.getId()); 523 } 524 525 // set the count or hits if in match mode 526 private void setCount(Object value, int row) { 527 ScheduleItem si = _list.get(row); 528 int count; 529 try { 530 count = Integer.parseInt(value.toString()); 531 } catch (NumberFormatException e) { 532 log.error("Schedule count must be a number"); 533 return; 534 } 535 if (count < 1) { 536 log.error("Schedule count must be greater than 0"); 537 return; 538 } 539 if (count > 100) { 540 log.warn("Schedule count must be 100 or less"); 541 count = 100; 542 } 543 si.setCount(count); 544 } 545 546 // set the count or hits if in match mode 547 private void setHit(Object value, int row) { 548 ScheduleItem si = _list.get(row); 549 int count; 550 try { 551 count = Integer.parseInt(value.toString()); 552 } catch (NumberFormatException e) { 553 log.error("Schedule hits must be a number"); 554 return; 555 } 556 // we don't care what value the user sets the hit count 557 si.setHits(count); 558 } 559 560 private void setWait(Object value, int row) { 561 ScheduleItem si = _list.get(row); 562 int wait; 563 try { 564 wait = Integer.parseInt(value.toString()); 565 } catch (NumberFormatException e) { 566 log.error("Schedule wait must be a number"); 567 return; 568 } 569 if (wait < 0) { 570 log.error("Schedule wait must be a positive number"); 571 return; 572 } 573 if (wait > 100) { 574 log.warn("Schedule wait must be 100 or less"); 575 wait = 100; 576 } 577 si.setWait(wait); 578 } 579 580 private void setRandom(Object value, int row) { 581 ScheduleItem si = _list.get(row); 582 String random = (String) ((JComboBox<?>) value).getSelectedItem(); 583 si.setRandom(random); 584 585 } 586 587 private void setSetoutDay(Object value, int row) { 588 ScheduleItem si = _list.get(row); 589 Object obj = ((JComboBox<?>) value).getSelectedItem(); 590 if (obj == null) { 591 si.setSetoutTrainScheduleId(ScheduleItem.NONE); 592 } else if (obj.getClass().equals(TrainSchedule.class)) { 593 si.setSetoutTrainScheduleId(((TrainSchedule) obj).getId()); 594 } 595 } 596 597 private void setPickupDay(Object value, int row) { 598 ScheduleItem si = _list.get(row); 599 Object obj = ((JComboBox<?>) value).getSelectedItem(); 600 if (obj == null) { 601 si.setPickupTrainScheduleId(ScheduleItem.NONE); 602 } else if (obj.getClass().equals(TrainSchedule.class)) { 603 si.setPickupTrainScheduleId(((TrainSchedule) obj).getId()); 604 } 605 } 606 607 // note this method looks for String "Not Valid <>" 608 private void setRoad(Object value, int row) { 609 ScheduleItem si = _list.get(row); 610 String road = (String) ((JComboBox<?>) value).getSelectedItem(); 611 if (checkForNotValidString(road)) { 612 si.setRoadName(road); 613 } 614 } 615 616 // note this method looks for String "Not Valid <>" 617 private void setLoad(Object value, int row) { 618 ScheduleItem si = _list.get(row); 619 String load = (String) ((JComboBox<?>) value).getSelectedItem(); 620 if (checkForNotValidString(load)) { 621 si.setReceiveLoadName(load); 622 } 623 } 624 625 // note this method looks for String "Not Valid <>" 626 private void setShip(Object value, int row) { 627 ScheduleItem si = _list.get(row); 628 String load = (String) ((JComboBox<?>) value).getSelectedItem(); 629 if (checkForNotValidString(load)) { 630 si.setShipLoadName(load); 631 } 632 } 633 634 /* 635 * Returns true if string is okay, doesn't have the string "Not Valid <>". 636 */ 637 private boolean checkForNotValidString(String s) { 638 if (s.length() < 12) { 639 return true; 640 } 641 String test = s.substring(0, 11); 642 if (test.equals(Bundle.getMessage("NotValid").substring(0, 11))) { 643 return false; 644 } 645 return true; 646 } 647 648 private void setDestination(Object value, int row) { 649 ScheduleItem si = _list.get(row); 650 si.setDestinationTrack(null); 651 Location dest = (Location) ((JComboBox<?>) value).getSelectedItem(); 652 si.setDestination(dest); 653 fireTableCellUpdated(row, TRACK_COLUMN); 654 } 655 656 private void setTrack(Object value, int row) { 657 ScheduleItem si = _list.get(row); 658 Track track = (Track) ((JComboBox<?>) value).getSelectedItem(); 659 si.setDestinationTrack(track); 660 } 661 662 private void moveUpScheduleItem(int row) { 663 log.debug("move schedule item up"); 664 _schedule.moveItemUp(_list.get(row)); 665 } 666 667 private void moveDownScheduleItem(int row) { 668 log.debug("move schedule item down"); 669 _schedule.moveItemDown(_list.get(row)); 670 } 671 672 private void deleteScheduleItem(int row) { 673 log.debug("Delete schedule item"); 674 _schedule.deleteItem(_list.get(row)); 675 } 676 677 // remove destinations that don't service the car's type 678 private void filterDestinations(JComboBox<Location> cb, String carType) { 679 for (int i = 1; i < cb.getItemCount(); i++) { 680 Location dest = cb.getItemAt(i); 681 if (!dest.acceptsTypeName(carType)) { 682 cb.removeItem(dest); 683 i--; 684 } 685 } 686 } 687 688 // remove destination tracks that don't service the car's type, road, or load 689 private void filterTracks(Location loc, JComboBox<Track> cb, String carType, String carRoad, String carLoad) { 690 List<Track> tracks = loc.getTracksList(); 691 for (Track track : tracks) { 692 if (!track.isTypeNameAccepted(carType) || 693 track.isStaging() || 694 (!carRoad.equals(ScheduleItem.NONE) && !track.isRoadNameAccepted(carRoad)) || 695 (!carLoad.equals(ScheduleItem.NONE) && !track.isLoadNameAndCarTypeAccepted(carLoad, carType))) { 696 cb.removeItem(track); 697 } 698 } 699 } 700 701 // remove receive loads not serviced by track 702 private void filterLoads(ScheduleItem si, JComboBox<String> cb) { 703 for (int i = cb.getItemCount() - 1; i > 0; i--) { 704 String loadName = cb.getItemAt(i); 705 if (!loadName.equals(CarLoads.NONE) && !_track.isLoadNameAndCarTypeAccepted(loadName, si.getTypeName())) { 706 cb.removeItem(loadName); 707 } 708 } 709 } 710 711 public void setMatchMode(boolean mode) { 712 if (mode != _matchMode) { 713 _matchMode = mode; 714 XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel(); 715 tcm.setColumnVisible(tcm.getColumnByModelIndex(HIT_COLUMN), mode); 716 tcm.setColumnVisible(tcm.getColumnByModelIndex(COUNT_COLUMN), !mode); 717 } 718 } 719 720 // this table listens for changes to a schedule and its car types 721 @Override 722 public void propertyChange(PropertyChangeEvent e) { 723 if (Control.SHOW_PROPERTY) { 724 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 725 .getNewValue()); 726 } 727 if (e.getPropertyName().equals(Schedule.LISTCHANGE_CHANGED_PROPERTY)) { 728 updateList(); 729 fireTableDataChanged(); 730 } 731 if (e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY) || 732 e.getPropertyName().equals(Track.ROADS_CHANGED_PROPERTY) || 733 e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) || 734 e.getPropertyName().equals(Track.SCHEDULE_CHANGED_PROPERTY) || 735 e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) || 736 e.getPropertyName().equals(Location.DISPOSE_CHANGED_PROPERTY)) { 737 fireTableDataChanged(); 738 } 739 // update hit count or other schedule item? 740 if (e.getSource().getClass().equals(ScheduleItem.class)) { 741 ScheduleItem item = (ScheduleItem) e.getSource(); 742 int row = _list.indexOf(item); 743 if (Control.SHOW_PROPERTY) { 744 log.debug("Update schedule item table row: {}", row); 745 } 746 if (row >= 0) { 747 fireTableRowsUpdated(row, row); 748 } 749 } 750 } 751 752 private void removePropertyChangeScheduleItems() { 753 for (ScheduleItem si : _list) { 754 si.removePropertyChangeListener(this); 755 if (si.getDestination() != null) { 756 si.getDestination().removePropertyChangeListener(this); 757 } 758 if (si.getDestinationTrack() != null) { 759 si.getDestinationTrack().removePropertyChangeListener(this); 760 } 761 } 762 } 763 764 public void dispose() { 765 if (_schedule != null) { 766 removePropertyChangeScheduleItems(); 767 _schedule.removePropertyChangeListener(this); 768 } 769 _location.removePropertyChangeListener(this); 770 _track.removePropertyChangeListener(this); 771 772 } 773 774 private final static Logger log = LoggerFactory.getLogger(ScheduleTableModel.class); 775}