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}