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