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