001package jmri.jmrit.operations.trains.tools; 002 003import java.awt.Dimension; 004import java.awt.GridBagLayout; 005import java.util.List; 006 007import javax.swing.*; 008 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import jmri.InstanceManager; 013import jmri.jmrit.operations.OperationsFrame; 014import jmri.jmrit.operations.locations.*; 015import jmri.jmrit.operations.locations.schedules.Schedule; 016import jmri.jmrit.operations.rollingstock.cars.*; 017import jmri.jmrit.operations.routes.Route; 018import jmri.jmrit.operations.routes.RouteLocation; 019import jmri.jmrit.operations.trains.Train; 020import jmri.jmrit.operations.trains.TrainManager; 021 022/** 023 * Frame to display by rolling stock, the locations serviced by this train 024 * 025 * @author Dan Boudreau Copyright (C) 2010, 2013, 2014 026 */ 027public class TrainByCarTypeFrame extends OperationsFrame implements java.beans.PropertyChangeListener { 028 029 protected static final String POINTER = " -->"; 030 031 // train 032 Train _train; 033 034 LocationManager locationManager = InstanceManager.getDefault(LocationManager.class); 035 036 // panels 037 JPanel pRoute = new JPanel(); 038 039 // radio buttons 040 // combo boxes 041 JComboBox<Train> trainsComboBox = InstanceManager.getDefault(TrainManager.class).getTrainComboBox(); 042 JComboBox<String> typeComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox(); 043 JComboBox<Car> carsComboBox = new JComboBox<>(); 044 045 // The car currently selected 046 Car _car; 047 048 /** 049 * Show how cars for a train can be serviced 050 * 051 * @param train the selected train 052 */ 053 public TrainByCarTypeFrame(Train train) { 054 super(); 055 _train = train; 056 initComponents(null); 057 } 058 059 /** 060 * Show how a car for a given train is serviced 061 * 062 * @param car the car being checked 063 */ 064 public TrainByCarTypeFrame(Car car) { 065 super(); 066 if (car != null) { 067 _train = car.getTrain(); 068 } 069 initComponents(car); 070 } 071 072 private void initComponents(Car car) { 073 getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 074 075 // Set up the panels 076 JPanel pTrain = new JPanel(); 077 pTrain.setLayout(new GridBagLayout()); 078 pTrain.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Train"))); 079 pTrain.setMaximumSize(new Dimension(2000, 50)); 080 081 addItem(pTrain, trainsComboBox, 0, 0); 082 trainsComboBox.setSelectedItem(_train); 083 084 JPanel pCarType = new JPanel(); 085 pCarType.setLayout(new GridBagLayout()); 086 pCarType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type"))); 087 pCarType.setMaximumSize(new Dimension(2000, 50)); 088 089 addItem(pCarType, typeComboBox, 0, 0); 090 addItem(pCarType, carsComboBox, 1, 0); 091 092 // increase width of combobox so large text names display properly 093 Dimension boxsize = typeComboBox.getMinimumSize(); 094 if (boxsize != null) { 095 boxsize.setSize(boxsize.width + 10, boxsize.height); 096 typeComboBox.setMinimumSize(boxsize); 097 } 098 099 adjustCarsComboBoxSize(); 100 101 pRoute.setLayout(new GridBagLayout()); 102 JScrollPane locationPane = new JScrollPane(pRoute); 103 locationPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 104 locationPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Route"))); 105 updateCarsComboBox(); 106 updateRoute(); 107 108 getContentPane().add(pTrain); 109 getContentPane().add(pCarType); 110 getContentPane().add(locationPane); 111 112 // setup combo box 113 addComboBoxAction(trainsComboBox); 114 addComboBoxAction(typeComboBox); 115 addComboBoxAction(carsComboBox); 116 117 if (car != null) { 118 typeComboBox.setSelectedItem(car.getTypeName()); 119 carsComboBox.setSelectedItem(car); 120 } 121 122 locationManager.addPropertyChangeListener(this); 123 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 124 // listen to all tracks and locations 125 addLocationAndTrackPropertyChange(); 126 127 addHelpMenu("package.jmri.jmrit.operations.Operations_TrainShowCarTypesServiced", true); // NOI18N 128 129 setPreferredSize(null); 130 initMinimumSize(); 131 } 132 133 @Override 134 public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) { 135 log.debug("combo box action"); 136 if (ae.getSource().equals(typeComboBox)) { 137 updateCarsComboBox(); 138 } 139 updateRoute(); 140 if (ae.getSource().equals(trainsComboBox)) { 141 pack(); 142 } 143 } 144 145 private void updateRoute() { 146 if (_train != null) { 147 _train.removePropertyChangeListener(this); 148 } 149 150 pRoute.removeAll(); 151 152 if (trainsComboBox.getSelectedItem() == null) { 153 _train = null; 154 } else { 155 _train = (Train) trainsComboBox.getSelectedItem(); 156 } 157 158 if (_train == null) { 159 setTitle(Bundle.getMessage("MenuItemShowCarTypes")); 160 repaint(); 161 return; 162 } 163 164 setTitle(Bundle.getMessage("MenuItemShowCarTypes") + " " + _train.getName()); 165 _train.addPropertyChangeListener(this); 166 log.debug("update locations served by train ({})", _train.getName()); 167 168 int y = 0; 169 String carType = (String) typeComboBox.getSelectedItem(); 170 if (_car != null) { 171 _car.removePropertyChangeListener(this); 172 } 173 _car = null; 174 if (carsComboBox.getSelectedItem() != null) { 175 _car = (Car) carsComboBox.getSelectedItem(); 176 _car.addPropertyChangeListener(this); 177 } 178 Route route = _train.getRoute(); 179 if (route == null) { 180 repaint(); 181 return; 182 } 183 List<RouteLocation> routeList = route.getLocationsBySequenceList(); 184 for (RouteLocation rl : routeList) { 185 JLabel loc = new JLabel(); 186 String locationName = rl.getName(); 187 loc.setText(locationName); 188 addItemLeft(pRoute, loc, 0, y++); 189 Location location = locationManager.getLocationByName(locationName); 190 if (location == null) { 191 continue; 192 } 193 if (_car != null && _car.getTrack() != null && !_car.getTrack().isDestinationAccepted(location)) { 194 JLabel locText = new JLabel(); 195 locText.setText(Bundle.getMessage("CarOnTrackDestinationRestriction", 196 _car.toString(), _car.getLocationName(), _car.getTrackName(), locationName)); 197 addItemWidth(pRoute, locText, 2, 1, y++); 198 if (_car.getLocation() != location) 199 continue; 200 } 201 List<Track> tracks = location.getTracksByNameList(null); 202 for (Track track : tracks) { 203 // show the car's track if there's a track destination 204 // restriction 205 if (_car != null && 206 _car.getTrack() != null && 207 !_car.getTrack().isDestinationAccepted(location) && 208 _car.getTrack() != track) { 209 continue; 210 } 211 JLabel trk = new JLabel(); 212 trk.setText(track.getName()); 213 addItemLeft(pRoute, trk, 1, y); 214 // is the car at this location and track? 215 if (_car != null && location.equals(_car.getLocation()) && track.equals(_car.getTrack())) { 216 JLabel here = new JLabel(POINTER); // NOI18N 217 addItemLeft(pRoute, here, 0, y); 218 } 219 JLabel op = new JLabel(); 220 addItemLeft(pRoute, op, 2, y++); 221 if (!_train.isTypeNameAccepted(carType)) { 222 op.setText(Bundle.getMessage("X(TrainType)")); 223 } else if (_car != null && !_train.isCarRoadNameAccepted(_car.getRoadName())) { 224 op.setText(Bundle.getMessage("X(TrainRoad)")); 225 } // TODO need to do the same tests for caboose changes in the 226 // train's route 227 else if (_car != null && 228 _car.isCaboose() && 229 _train.isCabooseNeeded() && 230 location.equals(_car.getLocation()) && 231 track.equals(_car.getTrack()) && 232 !_train.getCabooseRoad().equals(Train.NONE) && 233 !_car.getRoadName().equals(_train.getCabooseRoad()) && 234 location.getName().equals(_train.getTrainDepartsName())) { 235 op.setText(Bundle.getMessage("X(TrainRoad)")); 236 } else if (_car != null && 237 _car.hasFred() && 238 _train.isFredNeeded() && 239 location.equals(_car.getLocation()) && 240 track.equals(_car.getTrack()) && 241 !_train.getCabooseRoad().equals(Train.NONE) && 242 !_car.getRoadName().equals(_train.getCabooseRoad()) && 243 location.getName().equals(_train.getTrainDepartsName())) { 244 op.setText(Bundle.getMessage("X(TrainRoad)")); 245 } else if (_car != null && 246 !_car.isCaboose() && 247 !_car.isPassenger() && 248 !_train.isLoadNameAccepted(_car.getLoadName(), _car.getTypeName())) { 249 op.setText(Bundle.getMessage("X(TrainLoad)")); 250 } else if (_car != null && !_train.isBuiltDateAccepted(_car.getBuilt())) { 251 op.setText(Bundle.getMessage("X(TrainBuilt)")); 252 } else if (_car != null && !_train.isOwnerNameAccepted(_car.getOwnerName())) { 253 op.setText(Bundle.getMessage("X(TrainOwner)")); 254 } else if (_train.isLocationSkipped(rl.getId())) { 255 op.setText(Bundle.getMessage("X(TrainSkips)")); 256 } else if (!rl.isDropAllowed() && !rl.isPickUpAllowed()) { 257 op.setText(Bundle.getMessage("X(Route)")); 258 } else if (rl.getMaxCarMoves() <= 0) { 259 op.setText(Bundle.getMessage("X(RouteMoves)")); 260 } else if (!location.acceptsTypeName(carType)) { 261 op.setText(Bundle.getMessage("X(LocationType)")); 262 } // check route before checking train, check train calls check 263 // route 264 else if (!track.isPickupRouteAccepted(route) && !track.isDropRouteAccepted(route)) { 265 op.setText(Bundle.getMessage("X(TrackRoute)")); 266 } else if (!track.isPickupTrainAccepted(_train) && !track.isDropTrainAccepted(_train)) { 267 op.setText(Bundle.getMessage("X(TrackTrain)")); 268 } else if (!track.isTypeNameAccepted(carType)) { 269 op.setText(Bundle.getMessage("X(TrackType)")); 270 } else if (_car != null && !track.isRoadNameAccepted(_car.getRoadName())) { 271 op.setText(Bundle.getMessage("X(TrackRoad)")); 272 } else if (_car != null && 273 _car.getTrack() != track && 274 !track.isLoadNameAndCarTypeAccepted(_car.getLoadName(), _car.getTypeName())) { 275 op.setText(Bundle.getMessage("X(TrackLoad)")); 276 } else if (_car != null && 277 !track.isDestinationAccepted(_car.getFinalDestination()) && 278 _car.getDestination() == null) { 279 op.setText(Bundle.getMessage("X(TrackDestination)")); 280 } else if ((rl.getTrainDirection() & location.getTrainDirections()) == 0) { 281 op.setText(Bundle.getMessage("X(DirLoc)")); 282 } else if ((rl.getTrainDirection() & track.getTrainDirections()) == 0) { 283 op.setText(Bundle.getMessage("X(DirTrk)")); 284 } else if (!track.checkScheduleAttribute(Track.TYPE, carType, null)) { 285 op.setText(Bundle.getMessage("X(ScheduleType)")); 286 } else if (!track.checkScheduleAttribute(Track.LOAD, carType, _car)) { 287 op.setText(Bundle.getMessage("X(ScheduleLoad)")); 288 } else if (!track.checkScheduleAttribute(Track.ROAD, carType, _car)) { 289 op.setText(Bundle.getMessage("X(ScheduleRoad)")); 290 } else if (!track.checkScheduleAttribute(Track.TRAIN_SCHEDULE, carType, _car)) { 291 op.setText(Bundle.getMessage("X(ScheduleTrain)")); 292 } else if (!track.checkScheduleAttribute(Track.ALL, carType, _car)) { 293 op.setText(Bundle.getMessage("X(Schedule)")); 294 } else if (!track.isPickupTrainAccepted(_train)) { 295 // can the train drop off car? 296 if (rl.isDropAllowed() && track.isDropTrainAccepted(_train)) { 297 op.setText(Bundle.getMessage("DropOnly")); 298 } else { 299 op.setText(Bundle.getMessage("X(TrainPickup)")); 300 } 301 } else if (!track.isDropTrainAccepted(_train)) { 302 // can the train pick up car? 303 if (rl.isPickUpAllowed() && track.isPickupTrainAccepted(_train)) { 304 op.setText(Bundle.getMessage("PickupOnly")); 305 } else { 306 op.setText(Bundle.getMessage("X(TrainDrop)")); 307 } 308 // determine if a local move with default loads 309 } else if (!_train.isLocalSwitcher() && 310 _car != null && 311 _car.getTrack() != track && 312 _car.getLocation() == track.getLocation() && 313 _car.getFinalDestination() == null && 314 _car.getDivision() == null && 315 (_car.getLoadName().equals(InstanceManager.getDefault(CarLoads.class).getDefaultEmptyName()) || 316 _car.getLoadName() 317 .equals(InstanceManager.getDefault(CarLoads.class).getDefaultLoadName()))) { 318 op.setText(Bundle.getMessage("X(LocalMove)")); 319 // determine if local move with custom load or final 320 // destination is allowed 321 } else if (!_train.isAllowLocalMovesEnabled() && 322 _car != null && 323 _car.getTrack() != track && 324 _car.getLocation() == track.getLocation() && 325 (_car.getDivision() != null || 326 _car.getFinalDestination() != null || 327 !_car.getLoadName() 328 .equals(InstanceManager.getDefault(CarLoads.class).getDefaultEmptyName()) && 329 !_car.getLoadName().equals( 330 InstanceManager.getDefault(CarLoads.class).getDefaultLoadName()))) { 331 op.setText(Bundle.getMessage("X(TrainLocalMove)")); 332 // determine if spur can accept car with custom load 333 } else if (track.isSpur() && 334 track.getSchedule() == null && 335 _car != null && 336 _car.getTrack() != track && 337 !_car.getLoadName().equals(InstanceManager.getDefault(CarLoads.class).getDefaultEmptyName()) && 338 !_car.getLoadName().equals(InstanceManager.getDefault(CarLoads.class).getDefaultLoadName())) { 339 op.setText(Bundle.getMessage("X(TrackCustomLoad)")); 340 // empty car with a home division 341 } else if (_car != null && 342 _car.getDivision() != null && 343 _car.getLoadType().equals(CarLoad.LOAD_TYPE_EMPTY) && 344 _car.getDivision() != track.getDivision() && 345 !track.isInterchange()) { 346 op.setText(Bundle.getMessage("X(Division)")); 347 // loaded car with a home division 348 } else if (_car != null && 349 _car.getDivision() != null && 350 _car.getLoadType().equals(CarLoad.LOAD_TYPE_LOAD) && 351 _car.getDivision() != _car.getTrack().getDivision() && 352 _car.getDivision() != track.getDivision() && 353 !track.isInterchange()) { 354 op.setText(Bundle.getMessage("X(Division)")); 355 } else if (rl.isDropAllowed() && rl.isPickUpAllowed()) { 356 op.setText(Bundle.getMessage("ButtonOK")); 357 } else if (rl.isDropAllowed()) { 358 op.setText(Bundle.getMessage("DropOnly")); 359 } else if (rl.isPickUpAllowed()) { 360 op.setText(Bundle.getMessage("PickupOnly")); 361 } else { 362 op.setText("X"); // default shouldn't occur 363 } 364 } 365 } 366 pRoute.revalidate(); 367 repaint(); 368 } 369 370 private void updateComboBox() { 371 log.debug("update combobox"); 372 InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox); 373 } 374 375 private void updateCarsComboBox() { 376 log.debug("update car combobox"); 377 carsComboBox.removeAllItems(); 378 String carType = (String) typeComboBox.getSelectedItem(); 379 // load car combobox 380 carsComboBox.addItem(null); 381 List<Car> cars = InstanceManager.getDefault(CarManager.class).getByTypeList(carType); 382 for (Car car : cars) { 383 carsComboBox.addItem(car); 384 } 385 } 386 387 private void adjustCarsComboBoxSize() { 388 List<Car> cars = InstanceManager.getDefault(CarManager.class).getList(); 389 for (Car car : cars) { 390 carsComboBox.addItem(car); 391 } 392 Dimension boxsize = carsComboBox.getMinimumSize(); 393 if (boxsize != null) { 394 boxsize.setSize(boxsize.width + 10, boxsize.height); 395 carsComboBox.setMinimumSize(boxsize); 396 } 397 carsComboBox.removeAllItems(); 398 } 399 400 /** 401 * Add property listeners for locations and tracks 402 */ 403 private void addLocationAndTrackPropertyChange() { 404 for (Location loc : locationManager.getList()) { 405 loc.addPropertyChangeListener(this); 406 for (Track track : loc.getTracksList()) { 407 track.addPropertyChangeListener(this); 408 Schedule schedule = track.getSchedule(); 409 if (schedule != null) { 410 schedule.addPropertyChangeListener(this); 411 } 412 } 413 } 414 } 415 416 /** 417 * Remove property listeners for locations and tracks 418 */ 419 private void removeLocationAndTrackPropertyChange() { 420 for (Location loc : locationManager.getList()) { 421 loc.removePropertyChangeListener(this); 422 for (Track track : loc.getTracksList()) { 423 track.removePropertyChangeListener(this); 424 Schedule schedule = track.getSchedule(); 425 if (schedule != null) { 426 schedule.removePropertyChangeListener(this); 427 } 428 } 429 } 430 } 431 432 @Override 433 public void dispose() { 434 locationManager.removePropertyChangeListener(this); 435 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 436 removeLocationAndTrackPropertyChange(); 437 if (_train != null) { 438 _train.removePropertyChangeListener(this); 439 } 440 if (_car != null) { 441 _car.removePropertyChangeListener(this); 442 } 443 super.dispose(); 444 } 445 446 @Override 447 public void propertyChange(java.beans.PropertyChangeEvent e) { 448 log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N 449 if (e.getSource().equals(_car) || 450 e.getSource().equals(_train) || 451 e.getSource().getClass().equals(Track.class) || 452 e.getSource().getClass().equals(Location.class) || 453 e.getSource().getClass().equals(Schedule.class) || 454 e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY) || 455 e.getPropertyName().equals(Route.LISTCHANGE_CHANGED_PROPERTY)) { 456 updateRoute(); 457 } 458 if (e.getPropertyName().equals(Train.DISPOSE_CHANGED_PROPERTY)) { 459 dispose(); 460 } 461 if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) || 462 e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) { 463 updateComboBox(); 464 } 465 if (e.getPropertyName().equals(Location.LENGTH_CHANGED_PROPERTY)) { 466 // a track has been add or deleted update property listeners 467 removeLocationAndTrackPropertyChange(); 468 addLocationAndTrackPropertyChange(); 469 } 470 } 471 472 private final static Logger log = LoggerFactory.getLogger(TrainByCarTypeFrame.class); 473}