001package jmri.jmrit.operations.trains.tools; 002 003import java.awt.Dimension; 004import java.awt.GridBagLayout; 005import java.text.MessageFormat; 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.rollingstock.cars.Car; 015import jmri.jmrit.operations.rollingstock.cars.CarManager; 016import jmri.jmrit.operations.routes.RouteLocation; 017import jmri.jmrit.operations.setup.Control; 018import jmri.jmrit.operations.setup.Setup; 019import jmri.jmrit.operations.trains.*; 020 021/** 022 * Show Cars In Train Frame. This frame lists all cars assigned to a train in 023 * the correct blocking order. Also show which cars are to be picked up and set 024 * out at each location in the train's route. 025 * 026 * @author Dan Boudreau Copyright (C) 2012 027 */ 028public class ShowCarsInTrainFrame extends OperationsFrame implements java.beans.PropertyChangeListener { 029 030 Train _train = null; 031 CarManager carManager = InstanceManager.getDefault(CarManager.class); 032 TrainManager trainManager = InstanceManager.getDefault(TrainManager.class); 033 034 // labels 035 JLabel textTrainName = new JLabel(); 036 JLabel textLocationName = new JLabel(); 037 JLabel textNextLocationName = new JLabel(); 038 JTextPane textStatus = new JTextPane(); 039 JLabel textPickUp = new JLabel(Bundle.getMessage("Pickup")); 040 JLabel textInTrain = new JLabel(Bundle.getMessage("InTrain")); 041 JLabel textSetOut = new JLabel(Bundle.getMessage("SetOut")); 042 043 // panels 044 JPanel pCars = new JPanel(); 045 046 public ShowCarsInTrainFrame() { 047 super(); 048 } 049 050 public void initComponents(Train train) { 051 _train = train; 052 053 getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 054 055 JScrollPane carPane = new JScrollPane(pCars); 056 carPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Cars"))); 057 carPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 058 // carPane.setPreferredSize(new Dimension(200, 300)); 059 060 // Set up the panels 061 // Layout the panel by rows 062 // row 2 063 JPanel pRow2 = new JPanel(); 064 pRow2.setLayout(new BoxLayout(pRow2, BoxLayout.X_AXIS)); 065 066 // row 2a (train name) 067 JPanel pTrainName = new JPanel(); 068 pTrainName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Train"))); 069 pTrainName.add(textTrainName); 070 071 pRow2.add(pTrainName); 072 073 // row 6 074 JPanel pRow6 = new JPanel(); 075 pRow6.setLayout(new BoxLayout(pRow6, BoxLayout.X_AXIS)); 076 077 // row 10 078 JPanel pRow10 = new JPanel(); 079 pRow10.setLayout(new BoxLayout(pRow10, BoxLayout.X_AXIS)); 080 081 // row 10a (location name) 082 JPanel pLocationName = new JPanel(); 083 pLocationName.setBorder(BorderFactory.createTitledBorder("Location")); 084 pLocationName.add(textLocationName); 085 086 // row 10c (next location name) 087 JPanel pNextLocationName = new JPanel(); 088 pNextLocationName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("NextLocation"))); 089 pNextLocationName.add(textNextLocationName); 090 091 pRow10.add(pLocationName); 092 pRow10.add(pNextLocationName); 093 094 // row 12 095 JPanel pRow12 = new JPanel(); 096 pRow12.setLayout(new BoxLayout(pRow12, BoxLayout.X_AXIS)); 097 098 pCars.setLayout(new GridBagLayout()); 099 pRow12.add(carPane); 100 101 // row 13 102 // JPanel pStatus = new JPanel(); 103 // pStatus.setLayout(new GridBagLayout()); 104 textStatus.setBorder(BorderFactory.createTitledBorder("")); 105 // addItem(pStatus, textStatus, 0, 0); 106 textStatus.setBackground(null); 107 textStatus.setEditable(false); 108 109 getContentPane().add(pRow2); 110 getContentPane().add(pRow6); 111 getContentPane().add(pRow10); 112 getContentPane().add(pRow12); 113 getContentPane().add(textStatus); 114 115 if (_train != null) { 116 setTitle(Bundle.getMessage("TitleShowCarsInTrain") + " (" + _train.getName() + ")"); 117 118 // listen for train changes 119 _train.addPropertyChangeListener(this); 120 } 121 122 initMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight500)); 123 update(); 124 } 125 126 private void update() { 127 log.debug("queue update"); 128 // use invokeLater to prevent deadlock 129 SwingUtilities.invokeLater(() -> { 130 log.debug("update"); 131 if (_train == null || _train.getRoute() == null) { 132 return; 133 } 134 textTrainName.setText(_train.getIconName()); 135 pCars.removeAll(); 136 RouteLocation rl = _train.getCurrentRouteLocation(); 137 if (rl != null) { 138 textLocationName.setText(trainManager.isShowLocationHyphenNameEnabled() 139 ? rl.getLocation().getName() : rl.getLocation().getSplitName()); 140 textNextLocationName.setText(trainManager.isShowLocationHyphenNameEnabled() 141 ? _train.getNextLocationName() : TrainCommon.splitString(_train.getNextLocationName())); 142 // add header 143 int i = 0; 144 addItemLeft(pCars, textPickUp, 0, 0); 145 addItemLeft(pCars, textInTrain, 1, 0); 146 addItemLeft(pCars, textSetOut, 2, i++); 147 // block cars by destination 148 // caboose or FRED is placed at end of the train 149 // passenger cars are already blocked in the car list 150 // passenger cars with negative block numbers are placed at 151 // the front of the train, positive numbers at the end of 152 // the train. 153 for (RouteLocation rld : _train.getRoute().getLocationsBySequenceList()) { 154 for (Car car : carManager.getByTrainDestinationList(_train)) { 155 if (TrainCommon.isNextCar(car, rl, rld, true)) { 156 log.debug("car ({}) routelocation ({}) track ({}) route destination ({})", 157 car.toString(), car 158 .getRouteLocation().getName(), 159 car.getTrackName(), car.getRouteDestination().getName()); 160 JCheckBox checkBox = new JCheckBox(car.getRoadName().split(TrainCommon.HYPHEN)[0] + 161 " " + 162 TrainCommon.splitString(car.getNumber())); 163 if (car.getRouteDestination() == rl) { 164 addItemLeft(pCars, checkBox, 2, i++); // set out 165 } else if (car.getRouteLocation() == rl && car.getTrack() != null) { 166 addItemLeft(pCars, checkBox, 0, i++); // pick up 167 } else { 168 addItemLeft(pCars, checkBox, 1, i++); // in 169 // train 170 } 171 } 172 } 173 } 174 175 if (rl != _train.getTrainTerminatesRouteLocation()) { 176 textStatus.setText(getStatus(rl)); 177 } else { 178 textStatus.setText(MessageFormat.format(TrainManifestText.getStringTrainTerminates(), 179 new Object[]{_train.getTrainTerminatesName()})); 180 } 181 } 182 pCars.repaint(); 183 }); 184 } 185 186 private String getStatus(RouteLocation rl) { 187 if (Setup.isPrintLoadsAndEmptiesEnabled()) { 188 int emptyCars = _train.getNumberEmptyCarsInTrain(rl); 189 return MessageFormat.format(TrainManifestText.getStringTrainDepartsLoads(), new Object[]{ 190 rl.getSplitName(), rl.getTrainDirectionString(), 191 _train.getNumberCarsInTrain(rl) - emptyCars, emptyCars, _train.getTrainLength(rl), 192 Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl)}); 193 } else { 194 return MessageFormat.format(TrainManifestText.getStringTrainDepartsCars(), 195 new Object[]{rl.getSplitName(), rl.getTrainDirectionString(), _train.getNumberCarsInTrain(), 196 _train.getTrainLength(rl), Setup.getLengthUnit().toLowerCase(), 197 _train.getTrainWeight(rl)}); 198 } 199 } 200 201 @Override 202 public void dispose() { 203 if (_train != null) { 204 _train.removePropertyChangeListener(this); 205 } 206 super.dispose(); 207 } 208 209 @Override 210 public void propertyChange(java.beans.PropertyChangeEvent e) { 211 // if (Control.showProperty) 212 log.debug("Property change {} from: {} old: {} new: {}", e.getPropertyName(), e.getSource().toString(), 213 e.getOldValue(), e.getNewValue()); // NOI18N 214 if (e.getPropertyName().equals(Train.BUILT_CHANGED_PROPERTY) || 215 e.getPropertyName().equals(Train.TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY)) { 216 update(); 217 } 218 } 219 220 private final static Logger log = LoggerFactory.getLogger(ShowCarsInTrainFrame.class); 221}