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