001package jmri.jmrit.operations.locations.tools;
002
003import java.awt.Dimension;
004import java.awt.GridBagLayout;
005
006import javax.swing.*;
007
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
012import jmri.InstanceManager;
013import jmri.jmrit.operations.OperationsFrame;
014import jmri.jmrit.operations.locations.*;
015import jmri.jmrit.operations.rollingstock.cars.CarTypes;
016import jmri.jmrit.operations.routes.Route;
017import jmri.jmrit.operations.routes.RouteLocation;
018import jmri.jmrit.operations.setup.Control;
019import jmri.jmrit.operations.trains.Train;
020import jmri.jmrit.operations.trains.TrainManager;
021
022/**
023 * Frame to show which trains can service this location
024 *
025 * @author Dan Boudreau Copyright (C) 2014
026 */
027public class ShowTrainsServingLocationFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
028
029    Location _location = null;
030    Track _track = null;
031
032    // panels
033    JPanel pTrains = new JPanel();
034
035    // combo boxes
036    JComboBox<Location> locationComboBox = new JComboBox<>();
037    JComboBox<Track> trackComboBox = new JComboBox<>();
038    JComboBox<String> typeComboBox = new JComboBox<>();
039
040    // check boxes
041    JCheckBox showAllTrainsCheckBox = new JCheckBox(Bundle.getMessage("ShowAllTrains"));
042
043    // make show all trains consistent during a session
044    private static boolean isShowAllTrains = false;
045
046    public ShowTrainsServingLocationFrame() {
047        super(Bundle.getMessage("TitleShowTrains"));
048    }
049
050    public void initComponents(Location location, Track track) {
051
052        _location = location;
053        _track = track;
054
055        // general GUI config
056        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
057
058        // Set up the panels
059        JPanel pLocations = new JPanel();
060        pLocations.setLayout(new GridBagLayout());
061        pLocations.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location")));
062        pLocations.setMaximumSize(new Dimension(2000, 50));
063
064        addItem(pLocations, locationComboBox, 0, 0);
065
066        JPanel pTracks = new JPanel();
067        pTracks.setLayout(new GridBagLayout());
068        pTracks.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Track")));
069        pTracks.setMaximumSize(new Dimension(2000, 50));
070
071        addItem(pTracks, trackComboBox, 0, 0);
072
073        JPanel pCarType = new JPanel();
074        pCarType.setLayout(new GridBagLayout());
075        pCarType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
076        pCarType.setMaximumSize(new Dimension(2000, 50));
077
078        addItem(pCarType, typeComboBox, 0, 0);
079        
080        JPanel pOptions = new JPanel();
081        pOptions.setLayout(new GridBagLayout());
082        pOptions.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Options")));
083
084        addItem(pOptions, showAllTrainsCheckBox, 0, 0);
085
086        pTrains.setLayout(new GridBagLayout());
087        JScrollPane trainsPane = new JScrollPane(pTrains);
088        trainsPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
089        trainsPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Trains")));
090
091        getContentPane().add(pLocations);
092        getContentPane().add(pTracks);
093        getContentPane().add(pCarType);
094        getContentPane().add(pOptions);
095        getContentPane().add(trainsPane);
096
097        // show all trains
098        showAllTrainsCheckBox.setToolTipText(Bundle.getMessage("TipDeselectedShowAllTrains"));
099        addCheckBoxAction(showAllTrainsCheckBox);
100        showAllTrainsCheckBox.setSelected(isShowAllTrains);
101
102        // setup combo box
103        updateLocationsComboBox();
104        updateTracksComboBox();
105        updateTypeComboBox();
106
107        addComboBoxAction(locationComboBox);
108        addComboBoxAction(trackComboBox);
109        addComboBoxAction(typeComboBox);
110
111        // increase width of combobox so large text names display properly
112        Dimension boxsize = typeComboBox.getMinimumSize();
113        if (boxsize != null) {
114            boxsize.setSize(boxsize.width + 10, boxsize.height);
115            typeComboBox.setMinimumSize(boxsize);
116        }
117
118        if (location != null) {
119            location.addPropertyChangeListener(this);
120        }
121        if (track != null) {
122            track.addPropertyChangeListener(this);
123        }
124        addPropertyChangeAllTrains();
125
126        // add help menu to window
127        addHelpMenu("package.jmri.jmrit.operations.Operations_ShowTrainsServicingThisLocation", true); // NOI18N
128
129        setPreferredSize(null);
130        initMinimumSize();
131    }
132
133    private void updateTrainPane() {
134        log.debug("Updating for location ({}), Track ({})", _location, _track);
135        pTrains.removeAll();
136        int y = 0;
137        for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) {
138            Route route = train.getRoute();
139            if (route == null) {
140                continue;
141            }
142            // determine if the car type is accepted by train
143            boolean typeAccepted = train.isTypeNameAccepted(_carType);
144            if (_carType.equals(NONE)) {
145                // determine if any available car type is accepted by train
146                for (int i = 0; i < typeComboBox.getItemCount(); i++) {
147                    if (train.isTypeNameAccepted(typeComboBox.getItemAt(i))) {
148                        typeAccepted = true;
149                        break;
150                    }
151                }
152            }
153            for (RouteLocation rl : route.getLocationsBySequenceList()) {
154                if (_location != null && rl.getName().equals(_location.getName())) {
155                    boolean pickup = false;
156                    boolean setout = false;
157                    // monitor move count in the route for this location
158                    train.getRoute().removePropertyChangeListener(this);
159                    train.getRoute().addPropertyChangeListener(this);
160                    if (rl.isPickUpAllowed() &&
161                            rl.getMaxCarMoves() > 0 &&
162                            !train.isLocationSkipped(rl.getId()) &&
163                            typeAccepted &&
164                            (train.isLocalSwitcher() ||
165                                    (rl.getTrainDirection() & _location.getTrainDirections()) != 0) &&
166                            (train.isLocalSwitcher() ||
167                                    _track == null ||
168                                    ((rl.getTrainDirection() & _track.getTrainDirections()) != 0)) &&
169                            (_track == null || _track.isPickupTrainAccepted(train))) {
170                        pickup = true;
171                    }
172                    if (rl.isDropAllowed() &&
173                            rl.getMaxCarMoves() > 0 &&
174                            !train.isLocationSkipped(rl.getId()) &&
175                            typeAccepted &&
176                            (train.isLocalSwitcher() ||
177                                    (rl.getTrainDirection() & _location.getTrainDirections()) != 0) &&
178                            (train.isLocalSwitcher() ||
179                                    _track == null ||
180                                    ((rl.getTrainDirection() & _track.getTrainDirections()) != 0)) &&
181                            (_track == null || _track.isDropTrainAccepted(train)) &&
182                            (_track == null ||
183                                    _carType.equals(NONE) ||
184                                    _track.checkScheduleAttribute(Track.TYPE, _carType, null))) {
185                        setout = true;
186                    }
187                    // now display results
188                    if (showAllTrainsCheckBox.isSelected() || pickup || setout) {
189                        addItemLeft(pTrains, new JLabel(train.getName()), 0, y);
190                        // train direction when servicing this location
191                        addItem(pTrains, new JLabel(rl.getTrainDirectionString()), 1, y);
192                        if (pickup) {
193                            addItem(pTrains, new JLabel(Bundle.getMessage("OkayPickUp")), 2, y);
194                        } else {
195                            addItem(pTrains, new JLabel(Bundle.getMessage("NoPickUp")), 2, y);
196                        }
197                        if (setout) {
198                            addItem(pTrains, new JLabel(Bundle.getMessage("OkaySetOut")), 3, y);
199                        } else {
200                            addItem(pTrains, new JLabel(Bundle.getMessage("NoSetOut")), 3, y);
201                        }
202                    }
203                    y++;
204                }
205            }
206        }
207        pTrains.repaint();
208        pTrains.revalidate();
209        pack();
210    }
211
212    @Override
213    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "GUI ease of use")
214    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
215        log.debug("check box action");
216        isShowAllTrains = showAllTrainsCheckBox.isSelected();
217        updateTrainPane();
218    }
219
220    private String _carType = NONE;
221
222    @Override
223    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
224        if (ae.getSource().equals(locationComboBox)) {
225            _location = (Location) locationComboBox.getSelectedItem();
226            updateTracksComboBox();
227            updateTypeComboBox();
228            updateTrainPane();
229        }
230        if (ae.getSource().equals(trackComboBox)) {
231            if (_track != null) {
232                _track.removePropertyChangeListener(this);
233            }
234            _track = (Track) trackComboBox.getSelectedItem();
235            if (_track != null) {
236                _track.addPropertyChangeListener(this);
237            }
238            updateTypeComboBox();
239            updateTrainPane();
240        }
241        if (typeComboBox.isEnabled() && ae.getSource().equals(typeComboBox)) {
242            if (typeComboBox.getSelectedItem() != null) {
243                _carType = (String) typeComboBox.getSelectedItem();
244            }
245            updateTrainPane();
246        }
247    }
248
249    private void updateLocationsComboBox() {
250        InstanceManager.getDefault(LocationManager.class).updateComboBox(locationComboBox);
251        locationComboBox.setSelectedItem(_location);
252    }
253
254    private void updateTracksComboBox() {
255        if (_location != null) {
256            _location.updateComboBox(trackComboBox);
257        }
258        trackComboBox.setSelectedItem(_track);
259    }
260
261    private void updateTypeComboBox() {
262        log.debug("update type combobox");
263        typeComboBox.setEnabled(false);
264        InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox);
265        // remove car types not serviced by this location and track
266        for (int i = typeComboBox.getItemCount() - 1; i >= 0; i--) {
267            String type = typeComboBox.getItemAt(i);
268            if (_location != null && !_location.acceptsTypeName(type)) {
269                typeComboBox.removeItem(type);
270            }
271            if (_track != null && !_track.isTypeNameAccepted(type)) {
272                typeComboBox.removeItem(type);
273            }
274        }
275        typeComboBox.insertItemAt(NONE, 0);
276        typeComboBox.setSelectedItem(_carType);
277
278        updateTrainPane();
279        typeComboBox.setEnabled(true);
280    }
281
282    @Override
283    public void dispose() {
284        if (_location != null) {
285            _location.removePropertyChangeListener(this);
286        }
287        if (_track != null) {
288            _track.removePropertyChangeListener(this);
289        }
290        removePropertyChangeAllTrains();
291        super.dispose();
292    }
293
294    public void addPropertyChangeAllTrains() {
295        InstanceManager.getDefault(TrainManager.class).addPropertyChangeListener(this);
296        for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) {
297            train.addPropertyChangeListener(this);
298        }
299    }
300
301    public void removePropertyChangeAllTrains() {
302        InstanceManager.getDefault(TrainManager.class).removePropertyChangeListener(this);
303        for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) {
304            train.removePropertyChangeListener(this);
305            if (train.getRoute() != null) {
306                train.getRoute().removePropertyChangeListener(this);
307            }
308        }
309    }
310
311    @Override
312    public void propertyChange(java.beans.PropertyChangeEvent e) {
313        if (Control.SHOW_PROPERTY) {
314            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
315                    e.getNewValue());
316        }
317        if (e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) ||
318                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) {
319            updateTypeComboBox();
320        }
321        if (e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
322                e.getPropertyName().equals(Track.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
323                e.getPropertyName().equals(Track.DROP_CHANGED_PROPERTY) ||
324                e.getPropertyName().equals(Track.PICKUP_CHANGED_PROPERTY) ||
325                e.getPropertyName().equals(Train.TRAIN_ROUTE_CHANGED_PROPERTY) ||
326                e.getPropertyName().equals(Train.TYPES_CHANGED_PROPERTY) ||
327                e.getPropertyName().equals(Train.STOPS_CHANGED_PROPERTY) ||
328                e.getPropertyName().equals(Route.LISTCHANGE_CHANGED_PROPERTY)) {
329            updateTrainPane();
330        }
331        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY)) {
332            removePropertyChangeAllTrains();
333            addPropertyChangeAllTrains();
334        }
335    }
336
337    private final static Logger log = LoggerFactory.getLogger(ShowTrainsServingLocationFrame.class);
338}