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