001package jmri.jmrit.operations.locations.gui;
002
003import java.awt.Dimension;
004import java.awt.event.ActionEvent;
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.CommonConductorYardmasterPanel;
014import jmri.jmrit.operations.locations.Location;
015import jmri.jmrit.operations.rollingstock.RollingStock;
016import jmri.jmrit.operations.rollingstock.cars.Car;
017import jmri.jmrit.operations.routes.RouteLocation;
018import jmri.jmrit.operations.setup.Control;
019import jmri.jmrit.operations.setup.Setup;
020import jmri.jmrit.operations.trains.*;
021
022/**
023 * Yardmaster Frame. Shows work at one location.
024 *
025 * @author Dan Boudreau Copyright (C) 2013
026 * 
027 */
028public class YardmasterPanel extends CommonConductorYardmasterPanel {
029
030    int _visitNumber = 1;
031
032    // combo boxes
033    JComboBox<Train> trainComboBox = new JComboBox<>();
034    JComboBox<Integer> trainVisitComboBox = new JComboBox<>();
035
036    // buttons
037    JButton nextButton = new JButton(Bundle.getMessage("Next"));
038
039    // panels
040    JPanel pTrainVisit = new JPanel();
041
042    public YardmasterPanel() {
043        this(null);
044    }
045
046    public YardmasterPanel(Location location) {
047        super();
048
049        _location = location;
050
051        // row 2
052        JPanel pRow2 = new JPanel();
053        pRow2.setLayout(new BoxLayout(pRow2, BoxLayout.X_AXIS));
054
055        pRow2.add(pLocationName); // row 2a (location name)
056        pRow2.add(pRailRoadName); // row 2b (railroad name)
057
058        // row 6
059        JPanel pRow6 = new JPanel();
060        pRow6.setLayout(new BoxLayout(pRow6, BoxLayout.X_AXIS));
061
062        // row 6a (train name)
063        JPanel pTrainName = new JPanel();
064        pTrainName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Train")));
065        pTrainName.add(trainComboBox);
066        // add next button for web server
067        pTrainName.add(nextButton);
068
069        // row 6b (train visit)
070        pTrainVisit.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Visit")));
071        padComboBox(trainVisitComboBox, 2);
072        pTrainVisit.add(trainVisitComboBox);
073
074        pRow6.add(pTrainName);
075        pRow6.add(pTrainVisit);
076        pRow6.add(pTrainDescription); // row 6c (train description)
077
078        pButtons.setMaximumSize(new Dimension(2000, 200));
079
080        add(pRow2);
081        add(pRow6);
082        add(textLocationCommentPane);
083        add(textSwitchListCommentPane);
084        add(textTrainCommentPane);
085        add(textTrainRouteCommentPane);
086        add(textTrainRouteLocationCommentPane);
087        add(pTrackComments);
088        add(textTrainStatusPane);
089        add(locoPane);
090        add(pWorkPanes);
091        add(movePane);
092        add(pStatus);
093        add(pButtons);
094
095        if (_location != null) {
096            textLocationName.setText(_location.getName());
097            loadLocationComment(_location);
098            loadLocationSwitchListComment(_location);
099            updateTrainsComboBox();
100        }
101
102        update();
103
104        addComboBoxAction(trainComboBox);
105        addComboBoxAction(trainVisitComboBox);
106
107        addButtonAction(nextButton);
108
109        // listen for trains
110        addTrainListeners();
111    }
112
113    // Select, Clear, and Set Buttons
114    @Override
115    public void buttonActionPerformed(ActionEvent ae) {
116        if (ae.getSource() == nextButton) {
117            nextButtonAction();
118        }
119        super.buttonActionPerformed(ae);
120    }
121
122    private void nextButtonAction() {
123        log.debug("next button activated");
124        if (trainComboBox.getItemCount() > 1) {
125            if (pTrainVisit.isVisible()) {
126                int index = trainVisitComboBox.getSelectedIndex() + 1;
127                if (index < trainVisitComboBox.getItemCount()) {
128                    trainVisitComboBox.setSelectedIndex(index);
129                    return; // done
130                }
131            }
132            int index = trainComboBox.getSelectedIndex();
133            // index = -1 if first item (null) in trainComboBox
134            if (index == -1) {
135                index = 1;
136            } else {
137                index++;
138            }
139            if (index >= trainComboBox.getItemCount()) {
140                index = 0;
141            }
142            trainComboBox.setSelectedIndex(index);
143        }
144    }
145
146    // Select Train and Visit
147    @Override
148    protected void comboBoxActionPerformed(ActionEvent ae) {
149        // made the combo box not visible during updates, so ignore if not visible
150        if (ae.getSource() == trainComboBox && trainComboBox.isVisible()) {
151            _train = null;
152            if (trainComboBox.getSelectedItem() != null) {
153                _train = (Train) trainComboBox.getSelectedItem();
154                _visitNumber = 1;
155            }
156            clearAndUpdate();
157        }
158        // made the combo box not visible during updates, so ignore if not visible
159        if (ae.getSource() == trainVisitComboBox && trainVisitComboBox.isVisible()) {
160            if (trainVisitComboBox.getSelectedItem() != null) {
161                _visitNumber = (Integer) trainVisitComboBox.getSelectedItem();
162                clearAndUpdate();
163            }
164        }
165    }
166
167    @Override
168    protected void update() {
169        log.debug("queue update");
170        // use invokeLater to prevent deadlock
171        SwingUtilities.invokeLater(() -> {
172            log.debug("update, setMode: {}", isSetMode);
173            initialize();
174
175            // turn everything off and re-enable if needed
176            pButtons.setVisible(false);
177            pTrainVisit.setVisible(false);
178            trainVisitComboBox.setVisible(false); // Use visible as a flag to ignore updates
179            textTrainCommentPane.setVisible(false);
180            textTrainRouteCommentPane.setVisible(false);
181            textTrainRouteLocationCommentPane.setVisible(false);
182            textTrainStatusPane.setVisible(false);
183
184            textTrainDescription.setText("");
185            textStatus.setText("");
186
187            if (_train != null && _train.isBuilt() && _train.getRoute() != null) {
188                pButtons.setVisible(true);
189
190                loadTrainDescription();
191                loadTrainComment();
192                loadRouteComment();
193                loadRailroadName();
194
195                // determine how many times this train visits this location and if it is the
196                // last stop
197                RouteLocation rl = null;
198                List<RouteLocation> routeList = _train.getRoute().getLocationsBySequenceList();
199                int visitNumber = 0;
200                for (int i = 0; i < routeList.size(); i++) {
201                    if (routeList.get(i).getSplitName().equals(_location.getSplitName())) {
202                        visitNumber++;
203                        if (visitNumber == _visitNumber) {
204                            rl = routeList.get(i);
205                        }
206                    }
207                }
208
209                if (rl != null) {
210                    // update visit numbers
211                    if (visitNumber > 1) {
212                        trainVisitComboBox.removeAllItems(); // this fires an action change!
213                        for (int i = 0; i < visitNumber; i++) {
214                            trainVisitComboBox.addItem(i + 1);
215                        }
216                        trainVisitComboBox.setSelectedItem(_visitNumber);
217                        trainVisitComboBox.setVisible(true); // now pay attention to changes
218                        pTrainVisit.setVisible(true); // show the visit panel
219                    }
220
221                    if (Setup.isSwitchListRouteLocationCommentEnabled()) {
222                        loadRouteLocationComment(rl);
223                    }
224                    textLocationName.setText(rl.getLocation().getName()); // show name including hyphen and number
225
226                    updateTrackComments(rl, !IS_MANIFEST);
227                    
228                    String msg = TrainCommon.getSwitchListTrainStatus(_train, rl);
229                    textTrainStatusPane.setText(msg);
230                    textTrainStatusPane.setVisible(!msg.isBlank());
231
232                    // check for locos
233                    updateLocoPanes(rl);
234
235                    // now update the car pick ups and set outs
236                    blockCars(rl, !IS_MANIFEST);
237
238                    textStatus.setText(getStatus(rl, !IS_MANIFEST));
239                }
240                updateComplete();
241            }
242        });
243    }
244
245    private void updateTrainsComboBox() {
246        Object selectedItem = trainComboBox.getSelectedItem();
247        trainComboBox.setVisible(false); // used as a flag to ignore updates
248        trainComboBox.removeAllItems();
249        trainComboBox.addItem(null);
250        if (_location != null) {
251            List<Train> trains = trainManager.getTrainsArrivingThisLocationList(_location);
252            trains.stream().filter((train) -> (TrainCommon.isThereWorkAtLocation(train, _location)))
253                    .forEach((train) -> {
254                        trainComboBox.addItem(train);
255                    });
256        }
257        if (selectedItem != null) {
258            trainComboBox.setSelectedItem(selectedItem);
259        }
260        padComboBox(trainComboBox);
261        trainComboBox.setVisible(true);
262    }
263
264    private void addTrainListeners() {
265        log.debug("Adding train listerners");
266        List<Train> trains = InstanceManager.getDefault(TrainManager.class).getTrainsByIdList();
267        trains.stream().forEach((train) -> {
268            train.addPropertyChangeListener(this);
269        });
270        // listen for new trains being added
271        InstanceManager.getDefault(TrainManager.class).addPropertyChangeListener(this);
272    }
273
274    private void removeTrainListeners() {
275        log.debug("Removing train listerners");
276        List<Train> trains = InstanceManager.getDefault(TrainManager.class).getTrainsByIdList();
277        trains.stream().forEach((train) -> {
278            train.removePropertyChangeListener(this);
279        });
280        InstanceManager.getDefault(TrainManager.class).removePropertyChangeListener(this);
281    }
282
283    @Override
284    public void dispose() {
285        removeTrainListeners();
286        removePropertyChangeListerners();
287    }
288
289    @Override
290    public void propertyChange(java.beans.PropertyChangeEvent e) {
291        if (Control.SHOW_PROPERTY) {
292            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
293                    e.getNewValue());
294        }
295        if ((e.getPropertyName().equals(RollingStock.ROUTE_LOCATION_CHANGED_PROPERTY) && e.getNewValue() == null) ||
296                (e.getPropertyName().equals(RollingStock.ROUTE_DESTINATION_CHANGED_PROPERTY) &&
297                        e.getNewValue() == null) ||
298                e.getPropertyName().equals(RollingStock.TRAIN_CHANGED_PROPERTY) ||
299                e.getPropertyName().equals(Train.TRAIN_MODIFIED_CHANGED_PROPERTY)) {
300            // remove car from list
301            if (e.getSource().getClass().equals(Car.class)) {
302                Car car = (Car) e.getSource();
303                removeCarFromList(car);
304            }
305            update();
306        }
307        if (e.getPropertyName().equals(Train.BUILT_CHANGED_PROPERTY)) {
308            updateTrainsComboBox();
309        }
310        if (e.getPropertyName().equals(Train.TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY) && e.getSource() == _train) {
311            update();
312        }
313    }
314
315    private final static Logger log = LoggerFactory.getLogger(YardmasterPanel.class);
316}