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        // build menu
127        JMenuBar menuBar = new JMenuBar();
128        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
129        toolMenu.add(new PrintTrainsServingLocationAction(this, false));
130        toolMenu.add(new PrintTrainsServingLocationAction(this, true));
131        menuBar.add(toolMenu);
132        setJMenuBar(menuBar);
133        // add help menu to window
134        addHelpMenu("package.jmri.jmrit.operations.Operations_ShowTrainsServicingThisLocation", true); // NOI18N
135
136        setPreferredSize(null);
137        initMinimumSize();
138    }
139
140    private void updateTrainPane() {
141        log.debug("Updating for location ({}), Track ({})", _location, _track);
142        pTrains.removeAll();
143        int y = 0;
144        for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) {
145            Route route = train.getRoute();
146            if (route == null) {
147                continue;
148            }
149            // determine if the car type is accepted by train
150            boolean typeAccepted = train.isTypeNameAccepted(_carType);
151            if (_carType.equals(NONE)) {
152                // determine if any available car type is accepted by train
153                for (int i = 0; i < typeComboBox.getItemCount(); i++) {
154                    if (train.isTypeNameAccepted(typeComboBox.getItemAt(i))) {
155                        typeAccepted = true;
156                        break;
157                    }
158                }
159            }
160            for (RouteLocation rl : route.getLocationsBySequenceList()) {
161                if (_location != null && rl.getName().equals(_location.getName())) {
162                    boolean pickup = false;
163                    boolean setout = false;
164                    // monitor move count in the route for this location
165                    train.getRoute().removePropertyChangeListener(this);
166                    train.getRoute().addPropertyChangeListener(this);
167                    if (rl.isPickUpAllowed() &&
168                            rl.getMaxCarMoves() > 0 &&
169                            !train.isLocationSkipped(rl.getId()) &&
170                            typeAccepted &&
171                            (train.isLocalSwitcher() ||
172                                    (rl.getTrainDirection() & _location.getTrainDirections()) != 0) &&
173                            (train.isLocalSwitcher() ||
174                                    _track == null ||
175                                    ((rl.getTrainDirection() & _track.getTrainDirections()) != 0)) &&
176                            (_track == null || _track.isPickupTrainAccepted(train))) {
177                        pickup = true;
178                    }
179                    if (rl.isDropAllowed() &&
180                            rl.getMaxCarMoves() > 0 &&
181                            !train.isLocationSkipped(rl.getId()) &&
182                            typeAccepted &&
183                            (train.isLocalSwitcher() ||
184                                    (rl.getTrainDirection() & _location.getTrainDirections()) != 0) &&
185                            (train.isLocalSwitcher() ||
186                                    _track == null ||
187                                    ((rl.getTrainDirection() & _track.getTrainDirections()) != 0)) &&
188                            (_track == null || _track.isDropTrainAccepted(train)) &&
189                            (_track == null ||
190                                    _carType.equals(NONE) ||
191                                    _track.checkScheduleAttribute(Track.TYPE, _carType, null))) {
192                        setout = true;
193                    }
194                    // now display results
195                    if (showAllTrainsCheckBox.isSelected() || pickup || setout) {
196                        addItemLeft(pTrains, new JLabel(train.getName()), 0, y);
197                        // train direction when servicing this location
198                        addItem(pTrains, new JLabel(rl.getTrainDirectionString()), 1, y);
199                        if (pickup) {
200                            addItem(pTrains, new JLabel(Bundle.getMessage("OkayPickUp")), 2, y);
201                        } else {
202                            addItem(pTrains, new JLabel(Bundle.getMessage("NoPickUp")), 2, y);
203                        }
204                        if (setout) {
205                            addItem(pTrains, new JLabel(Bundle.getMessage("OkaySetOut")), 3, y);
206                        } else {
207                            addItem(pTrains, new JLabel(Bundle.getMessage("NoSetOut")), 3, y);
208                        }
209                    }
210                    y++;
211                }
212            }
213        }
214        pTrains.repaint();
215        pTrains.revalidate();
216        pack();
217    }
218
219    @Override
220    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "GUI ease of use")
221    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
222        log.debug("check box action");
223        isShowAllTrains = showAllTrainsCheckBox.isSelected();
224        updateTrainPane();
225    }
226
227    String _carType = NONE;
228
229    @Override
230    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
231        if (ae.getSource().equals(locationComboBox)) {
232            _location = (Location) locationComboBox.getSelectedItem();
233            updateTracksComboBox();
234            updateTypeComboBox();
235            updateTrainPane();
236        }
237        if (ae.getSource().equals(trackComboBox)) {
238            if (_track != null) {
239                _track.removePropertyChangeListener(this);
240            }
241            _track = (Track) trackComboBox.getSelectedItem();
242            if (_track != null) {
243                _track.addPropertyChangeListener(this);
244            }
245            updateTypeComboBox();
246            updateTrainPane();
247        }
248        if (typeComboBox.isEnabled() && ae.getSource().equals(typeComboBox)) {
249            if (typeComboBox.getSelectedItem() != null) {
250                _carType = (String) typeComboBox.getSelectedItem();
251            }
252            updateTrainPane();
253        }
254    }
255
256    private void updateLocationsComboBox() {
257        InstanceManager.getDefault(LocationManager.class).updateComboBox(locationComboBox);
258        locationComboBox.setSelectedItem(_location);
259    }
260
261    private void updateTracksComboBox() {
262        if (_location != null) {
263            _location.updateComboBox(trackComboBox);
264        }
265        trackComboBox.setSelectedItem(_track);
266    }
267
268    private void updateTypeComboBox() {
269        log.debug("update type combobox");
270        typeComboBox.setEnabled(false);
271        InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox);
272        // remove car types not serviced by this location and track
273        for (int i = typeComboBox.getItemCount() - 1; i >= 0; i--) {
274            String type = typeComboBox.getItemAt(i);
275            if (_location != null && !_location.acceptsTypeName(type)) {
276                typeComboBox.removeItem(type);
277            }
278            if (_track != null && !_track.isTypeNameAccepted(type)) {
279                typeComboBox.removeItem(type);
280            }
281        }
282        typeComboBox.insertItemAt(NONE, 0);
283        typeComboBox.setSelectedItem(_carType);
284
285        updateTrainPane();
286        typeComboBox.setEnabled(true);
287    }
288
289    @Override
290    public void dispose() {
291        if (_location != null) {
292            _location.removePropertyChangeListener(this);
293        }
294        if (_track != null) {
295            _track.removePropertyChangeListener(this);
296        }
297        removePropertyChangeAllTrains();
298        super.dispose();
299    }
300
301    public void addPropertyChangeAllTrains() {
302        InstanceManager.getDefault(TrainManager.class).addPropertyChangeListener(this);
303        for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) {
304            train.addPropertyChangeListener(this);
305        }
306    }
307
308    public void removePropertyChangeAllTrains() {
309        InstanceManager.getDefault(TrainManager.class).removePropertyChangeListener(this);
310        for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByNameList()) {
311            train.removePropertyChangeListener(this);
312            if (train.getRoute() != null) {
313                train.getRoute().removePropertyChangeListener(this);
314            }
315        }
316    }
317
318    @Override
319    public void propertyChange(java.beans.PropertyChangeEvent e) {
320        if (Control.SHOW_PROPERTY) {
321            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
322                    e.getNewValue());
323        }
324        if (e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) ||
325                e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY)) {
326            updateTypeComboBox();
327        }
328        if (e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
329                e.getPropertyName().equals(Track.TRAIN_DIRECTION_CHANGED_PROPERTY) ||
330                e.getPropertyName().equals(Track.DROP_CHANGED_PROPERTY) ||
331                e.getPropertyName().equals(Track.PICKUP_CHANGED_PROPERTY) ||
332                e.getPropertyName().equals(Train.TRAIN_ROUTE_CHANGED_PROPERTY) ||
333                e.getPropertyName().equals(Train.TYPES_CHANGED_PROPERTY) ||
334                e.getPropertyName().equals(Train.STOPS_CHANGED_PROPERTY) ||
335                e.getPropertyName().equals(Route.LISTCHANGE_CHANGED_PROPERTY)) {
336            updateTrainPane();
337        }
338        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY)) {
339            removePropertyChangeAllTrains();
340            addPropertyChangeAllTrains();
341        }
342    }
343
344    private final static Logger log = LoggerFactory.getLogger(ShowTrainsServingLocationFrame.class);
345}