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