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