001package jmri.jmrit.operations.rollingstock;
002
003import java.awt.GridBagLayout;
004import java.text.MessageFormat;
005import java.util.List;
006import java.util.ResourceBundle;
007
008import javax.swing.*;
009
010import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
011import jmri.InstanceManager;
012import jmri.jmrit.operations.OperationsFrame;
013import jmri.jmrit.operations.locations.*;
014import jmri.jmrit.operations.rollingstock.cars.Car;
015import jmri.jmrit.operations.rollingstock.cars.tools.CarsSetFrame;
016import jmri.jmrit.operations.rollingstock.engines.tools.EnginesSetFrame;
017import jmri.jmrit.operations.routes.Route;
018import jmri.jmrit.operations.routes.RouteLocation;
019import jmri.jmrit.operations.setup.Setup;
020import jmri.jmrit.operations.trains.Train;
021import jmri.jmrit.operations.trains.TrainManager;
022import jmri.util.swing.JmriJOptionPane;
023
024/**
025 * Frame for user to place RollingStock on the layout
026 *
027 * @author Dan Boudreau Copyright (C) 2010, 2011, 2012, 2013
028 * @param <T> the type of RollingStock supported by this frame
029 */
030public abstract class RollingStockSetFrame<T extends RollingStock> extends OperationsFrame implements java.beans.PropertyChangeListener {
031
032    protected LocationManager locationManager = InstanceManager.getDefault(LocationManager.class);
033    protected TrainManager trainManager = InstanceManager.getDefault(TrainManager.class);
034
035    RollingStock _rs;
036
037    // labels
038    JLabel textRoad = new JLabel();
039    JLabel textType = new JLabel();
040
041    // major buttons
042    public JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
043    public JButton ignoreAllButton = new JButton(Bundle.getMessage("IgnoreAll"));
044
045    // combo boxes
046    public JComboBox<Location> locationBox = locationManager.getComboBox();
047    public JComboBox<Track> trackLocationBox = new JComboBox<>();
048    public JComboBox<Location> destinationBox = locationManager.getComboBox();
049    public JComboBox<Track> trackDestinationBox = new JComboBox<>();
050    public JComboBox<Location> finalDestinationBox = locationManager.getComboBox();
051    public JComboBox<Track> finalDestTrackBox = new JComboBox<>();
052    public JComboBox<Train> trainBox = trainManager.getTrainComboBox();
053
054    // check boxes
055    public JCheckBox autoTrackCheckBox = new JCheckBox(Bundle.getMessage("Auto"));
056    public JCheckBox autoDestinationTrackCheckBox = new JCheckBox(Bundle.getMessage("Auto"));
057    public JCheckBox autoFinalDestTrackCheckBox = new JCheckBox(Bundle.getMessage("Auto"));
058    public JCheckBox autoTrainCheckBox = new JCheckBox(Bundle.getMessage("Auto"));
059
060    public JCheckBox locationUnknownCheckBox = new JCheckBox(Bundle.getMessage("LocationUnknown"));
061    public JCheckBox outOfServiceCheckBox = new JCheckBox(Bundle.getMessage("OutOfService"));
062
063    public JCheckBox ignoreStatusCheckBox = new JCheckBox(Bundle.getMessage("Ignore"));
064    public JCheckBox ignoreLocationCheckBox = new JCheckBox(Bundle.getMessage("Ignore"));
065    public JCheckBox ignoreDestinationCheckBox = new JCheckBox(Bundle.getMessage("Ignore"));
066    public JCheckBox ignoreFinalDestinationCheckBox = new JCheckBox(Bundle.getMessage("Ignore"));
067    public JCheckBox ignoreTrainCheckBox = new JCheckBox(Bundle.getMessage("Ignore"));
068
069    // optional panels
070    protected JPanel pOptional = new JPanel();
071    protected JScrollPane paneOptional = new JScrollPane(pOptional);
072    protected JPanel pFinalDestination = new JPanel();
073
074    // Auto checkbox states
075    private static boolean autoTrackCheckBoxSelected = false;
076    private static boolean autoDestinationTrackCheckBoxSelected = false;
077    private static boolean autoFinalDestTrackCheckBoxSelected = false;
078    private static boolean autoTrainCheckBoxSelected = false;
079
080    public RollingStockSetFrame(String title) {
081        super(title);
082    }
083
084    @Override
085    public void initComponents() {
086        // the following code sets the frame's initial state
087        // create panel
088        JPanel pPanel = new JPanel();
089        pPanel.setLayout(new BoxLayout(pPanel, BoxLayout.Y_AXIS));
090
091        // Layout the panel by rows
092        // row 1
093        JPanel pRow1 = new JPanel();
094        pRow1.setLayout(new BoxLayout(pRow1, BoxLayout.X_AXIS));
095        // row 1a
096        JPanel pRs = new JPanel();
097        pRs.setLayout(new GridBagLayout());
098        pRs.setBorder(BorderFactory.createTitledBorder(getRb().getString("rsType")));
099        addItem(pRs, textRoad, 1, 0);
100        pRow1.add(pRs);
101
102        // row 1b
103        JPanel pType = new JPanel();
104        pType.setLayout(new GridBagLayout());
105        pType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type")));
106        addItem(pType, textType, 1, 0);
107        pRow1.add(pType);
108
109        // row 1c
110        JPanel pStatus = new JPanel();
111        pStatus.setLayout(new GridBagLayout());
112        pStatus.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Status")));
113        addItemLeft(pStatus, ignoreStatusCheckBox, 0, 0);
114        addItemLeft(pStatus, locationUnknownCheckBox, 1, 1);
115        addItemLeft(pStatus, outOfServiceCheckBox, 1, 0);
116        pRow1.add(pStatus);
117
118        pPanel.add(pRow1);
119
120        // row 2
121        JPanel pLocation = new JPanel();
122        pLocation.setLayout(new GridBagLayout());
123        pLocation.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocationAndTrack")));
124        addItemLeft(pLocation, ignoreLocationCheckBox, 0, 1);
125        addItem(pLocation, locationBox, 1, 1);
126        trackLocationBox.setName("trackLocationBox");
127        addItem(pLocation, trackLocationBox, 2, 1);
128        addItem(pLocation, autoTrackCheckBox, 3, 1);
129        pPanel.add(pLocation);
130
131        // optional panel 2
132        JPanel pOptional2 = new JPanel();
133        JScrollPane paneOptional2 = new JScrollPane(pOptional2);
134        pOptional2.setLayout(new BoxLayout(pOptional2, BoxLayout.Y_AXIS));
135        paneOptional2.setBorder(BorderFactory.createTitledBorder(Bundle
136                .getMessage("BorderLayoutOptionalProgram")));
137
138        // row 6
139        JPanel pDestination = new JPanel();
140        pDestination.setLayout(new GridBagLayout());
141        pDestination.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("DestinationAndTrack")));
142        addItemLeft(pDestination, ignoreDestinationCheckBox, 0, 1);
143        addItem(pDestination, destinationBox, 1, 1);
144        addItem(pDestination, trackDestinationBox, 2, 1);
145        addItem(pDestination, autoDestinationTrackCheckBox, 3, 1);
146        pOptional2.add(pDestination);
147
148        // row 7
149        pFinalDestination.setLayout(new GridBagLayout());
150        pFinalDestination.setBorder(BorderFactory.createTitledBorder(Bundle
151                .getMessage("FinalDestinationAndTrack")));
152        addItemLeft(pFinalDestination, ignoreFinalDestinationCheckBox, 0, 1);
153        addItem(pFinalDestination, finalDestinationBox, 1, 1);
154        addItem(pFinalDestination, finalDestTrackBox, 2, 1);
155        addItem(pFinalDestination, autoFinalDestTrackCheckBox, 3, 1);
156        pOptional2.add(pFinalDestination);
157
158        // row 8
159        JPanel pTrain = new JPanel();
160        pTrain.setLayout(new GridBagLayout());
161        pTrain.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Train")));
162        addItemLeft(pTrain, ignoreTrainCheckBox, 0, 0);
163        addItem(pTrain, trainBox, 1, 0);
164        addItem(pTrain, autoTrainCheckBox, 2, 0);
165        pOptional2.add(pTrain);
166
167        // button panel
168        JPanel pButtons = new JPanel();
169        pButtons.setLayout(new GridBagLayout());
170        addItem(pButtons, ignoreAllButton, 1, 0);
171        addItem(pButtons, saveButton, 2, 0);
172
173        // add panels
174        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
175        getContentPane().add(pPanel);
176        getContentPane().add(paneOptional);
177        getContentPane().add(paneOptional2);
178        getContentPane().add(pButtons);
179
180        // Don't show ignore buttons
181        ignoreStatusCheckBox.setVisible(false);
182        ignoreLocationCheckBox.setVisible(false);
183        ignoreDestinationCheckBox.setVisible(false);
184        ignoreFinalDestinationCheckBox.setVisible(false);
185        ignoreTrainCheckBox.setVisible(false);
186        ignoreAllButton.setVisible(false);
187
188        // setup buttons
189        addButtonAction(ignoreAllButton);
190        addButtonAction(saveButton);
191
192        // setup combobox
193        addComboBoxAction(locationBox);
194        addComboBoxAction(destinationBox);
195        addComboBoxAction(finalDestinationBox);
196        addComboBoxAction(trainBox);
197
198        // setup checkbox
199        addCheckBoxAction(locationUnknownCheckBox);
200        addCheckBoxAction(outOfServiceCheckBox);
201        addCheckBoxAction(autoTrackCheckBox);
202        addCheckBoxAction(autoDestinationTrackCheckBox);
203        addCheckBoxAction(autoFinalDestTrackCheckBox);
204        addCheckBoxAction(autoTrainCheckBox);
205
206        addCheckBoxAction(ignoreStatusCheckBox);
207        addCheckBoxAction(ignoreLocationCheckBox);
208        addCheckBoxAction(ignoreDestinationCheckBox);
209        addCheckBoxAction(ignoreFinalDestinationCheckBox);
210        addCheckBoxAction(ignoreTrainCheckBox);
211
212        // set auto check box selected
213        autoTrackCheckBox.setSelected(autoTrackCheckBoxSelected);
214        autoDestinationTrackCheckBox.setSelected(autoDestinationTrackCheckBoxSelected);
215        autoFinalDestTrackCheckBox.setSelected(autoFinalDestTrackCheckBoxSelected);
216        autoTrainCheckBox.setSelected(autoTrainCheckBoxSelected);
217
218        // add tool tips
219        autoTrackCheckBox.setToolTipText(getRb().getString("rsTipAutoTrack"));
220        autoDestinationTrackCheckBox.setToolTipText(getRb().getString("rsTipAutoTrack"));
221        autoFinalDestTrackCheckBox.setToolTipText(getRb().getString("rsTipAutoTrack"));
222        autoTrainCheckBox.setToolTipText(Bundle.getMessage("rsTipAutoTrain"));
223        locationUnknownCheckBox.setToolTipText(Bundle.getMessage("TipLocationUnknown"));
224
225        ignoreStatusCheckBox.setToolTipText(Bundle.getMessage("TipIgnore"));
226        ignoreLocationCheckBox.setToolTipText(Bundle.getMessage("TipIgnore"));
227        ignoreDestinationCheckBox.setToolTipText(Bundle.getMessage("TipIgnore"));
228        ignoreFinalDestinationCheckBox.setToolTipText(Bundle.getMessage("TipIgnore"));
229        ignoreTrainCheckBox.setToolTipText(Bundle.getMessage("TipIgnore"));
230
231        // get notified if combo box gets modified
232        locationManager.addPropertyChangeListener(this);
233        // get notified if train combo box gets modified
234        trainManager.addPropertyChangeListener(this);
235    }
236
237    protected void load(RollingStock rs) {
238        _rs = rs;
239        textRoad.setText(_rs.getRoadName() + " " + _rs.getNumber());
240        textType.setText(_rs.getTypeName());
241        locationUnknownCheckBox.setSelected(_rs.isLocationUnknown());
242        outOfServiceCheckBox.setSelected(_rs.isOutOfService());
243        updateComboBoxes(); // load the location, destination, and final destination combo boxes
244        updateTrainComboBox(); // load the train combo box
245        enableComponents(!locationUnknownCheckBox.isSelected());
246        // has the program generated a pick up and set out for this rolling stock?
247        if (_rs.getTrain() != null &&
248                _rs.getTrain().isBuilt() &&
249                (_rs.getRouteLocation() != null || _rs.getRouteDestination() != null)) {
250            if (_rs.getRouteLocation() != null) {
251                log.debug("rs ({}) has a pick up location ({})", _rs.toString(), _rs.getRouteLocation().getName());
252            }
253            if (_rs.getRouteDestination() != null) {
254                log.debug("rs ({}) has a destination ({})", _rs.toString(), _rs.getRouteDestination().getName());
255            }
256            if (getClass() == CarsSetFrame.class || getClass() == EnginesSetFrame.class) {
257                JmriJOptionPane.showMessageDialog(this, getRb().getString("rsPressChangeWill"), getRb().getString(
258                        "rsInRoute"), JmriJOptionPane.WARNING_MESSAGE);
259            } else {
260                JmriJOptionPane.showMessageDialog(this, getRb().getString("rsPressSaveWill"), getRb().getString(
261                        "rsInRoute"), JmriJOptionPane.WARNING_MESSAGE);
262            }
263        }
264        _rs.addPropertyChangeListener(this);
265    }
266
267    // Save button
268    @Override
269    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
270        if (ae.getSource() == saveButton) {
271            if (save() && Setup.isCloseWindowOnSaveEnabled()) {
272                dispose();
273            }
274        }
275    }
276
277    abstract protected ResourceBundle getRb();
278
279    protected boolean save() {
280        return change(_rs);
281    }
282
283    // change(RollingStock rs) will load the route location and the route destination if possible
284    RouteLocation rl;
285    RouteLocation rd;
286
287    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "GUI ease of use")
288    protected boolean change(RollingStock rs) {
289        log.debug("Change button action for rs ({})", rs.toString());
290        // save the auto buttons
291        autoTrackCheckBoxSelected = autoTrackCheckBox.isSelected();
292        autoDestinationTrackCheckBoxSelected = autoDestinationTrackCheckBox.isSelected();
293        autoFinalDestTrackCheckBoxSelected = autoFinalDestTrackCheckBox.isSelected();
294        autoTrainCheckBoxSelected = autoTrainCheckBox.isSelected();
295
296        // save the statuses
297        if (!ignoreStatusCheckBox.isSelected()) {
298            rs.setLocationUnknown(locationUnknownCheckBox.isSelected());
299            rs.setOutOfService(outOfServiceCheckBox.isSelected());
300        }
301        // update location
302        if (!changeLocation(rs)) {
303            return false;
304        }
305        // check to see if rolling stock is in staging and out of service (also location unknown)
306        if (outOfServiceCheckBox.isSelected() && rs.getTrack() != null && rs.getTrack().isStaging()) {
307            JmriJOptionPane.showMessageDialog(this, getRb().getString("rsNeedToRemoveStaging"), getRb()
308                    .getString("rsInStaging"), JmriJOptionPane.WARNING_MESSAGE);
309            // clear the rolling stock's location
310            rs.setLocation(null, null);
311        }
312
313        loadTrain(rs);
314
315        // update destination
316        if (!changeDestination(rs)) {
317            return false;
318        }
319
320        updateTrainComboBox();
321
322        // check if train can service this rolling stock
323        if (!ignoreTrainCheckBox.isSelected()) {
324            Train train = rs.getTrain();
325            if (train != null) {
326                // determine if train services this rs's type
327                if (!train.isTypeNameAccepted(rs.getTypeName())) {
328                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
329                            "rsTrainNotServType"), new Object[]{rs.getTypeName(), train.getName()}), getRb()
330                                    .getString("rsNotMove"),
331                            JmriJOptionPane.ERROR_MESSAGE);
332                    // prevent rs from being picked up and delivered
333                    setRouteLocationAndDestination(rs, train, null, null);
334                    return false;
335                }
336                // determine if train services this rs's road
337                if (rs.getClass() == Car.class) {
338                    Car car = (Car) rs;
339                    if (!car.isCaboose() && !train.isCarRoadNameAccepted(car.getRoadName()) ||
340                            car.isCaboose() && !train.isCabooseRoadNameAccepted(car.getRoadName())) {
341                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
342                                "rsTrainNotServRoad"), new Object[]{rs.getRoadName(), train.getName()}), getRb()
343                                        .getString("rsNotMove"),
344                                JmriJOptionPane.ERROR_MESSAGE);
345                        // prevent rs from being picked up and delivered
346                        setRouteLocationAndDestination(rs, train, null, null);
347                        return false;
348                    }
349                } else if (!train.isLocoRoadNameAccepted(rs.getRoadName())) {
350                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
351                            "rsTrainNotServRoad"), new Object[]{rs.getRoadName(), train.getName()}), getRb()
352                                    .getString("rsNotMove"),
353                            JmriJOptionPane.ERROR_MESSAGE);
354                    // prevent rs from being picked up and delivered
355                    setRouteLocationAndDestination(rs, train, null, null);
356                    return false;
357                }
358                // determine if train services this rs's built date
359                if (!train.isBuiltDateAccepted(rs.getBuilt())) {
360                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
361                            "rsTrainNotServBuilt"), new Object[]{rs.getBuilt(), train.getName()}), getRb()
362                                    .getString("rsNotMove"),
363                            JmriJOptionPane.ERROR_MESSAGE);
364                    // prevent rs from being picked up and delivered
365                    setRouteLocationAndDestination(rs, train, null, null);
366                    return false;
367                }
368                // determine if train services this rs's owner
369                if (!train.isOwnerNameAccepted(rs.getOwnerName())) {
370                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
371                            "rsTrainNotServOwner"), new Object[]{rs.getOwnerName(), train.getName()}), getRb()
372                                    .getString("rsNotMove"),
373                            JmriJOptionPane.ERROR_MESSAGE);
374                    // prevent rs from being picked up and delivered
375                    setRouteLocationAndDestination(rs, train, null, null);
376                    return false;
377                }
378                // determine if train services the location and destination selected by user
379                rl = null;
380                rd = null;
381                if (rs.getLocation() != null) {
382                    Route route = train.getRoute();
383                    if (route != null) {
384                        // this is a quick check, the actual rl and rd are set later in this routine.
385                        rl = route.getLastLocationByName(rs.getLocationName());
386                        rd = route.getLastLocationByName(rs.getDestinationName());
387                    }
388                    if (rl == null) {
389                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
390                                "rsLocNotServ"), new Object[]{rs.getLocationName(), train.getName()}),
391                                getRb().getString("rsNotMove"), JmriJOptionPane.ERROR_MESSAGE);
392                        // prevent rs from being picked up and delivered
393                        setRouteLocationAndDestination(rs, train, null, null);
394                        return false;
395                    }
396                    if (rd == null && !rs.getDestinationName().equals(RollingStock.NONE)) {
397                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
398                                "rsDestNotServ"), new Object[]{rs.getDestinationName(), train.getName()}),
399                                getRb().getString("rsNotMove"), JmriJOptionPane.ERROR_MESSAGE);
400                        // prevent rs from being picked up and delivered
401                        setRouteLocationAndDestination(rs, train, null, null);
402                        return false;
403                    }
404                    if (rd != null && route != null) {
405                        // now determine if destination is after location
406                        List<RouteLocation> routeSequence = route.getLocationsBySequenceList();
407                        boolean foundTrainLoc = false; // when true, found the train's location
408                        boolean foundLoc = false; // when true, found the rs's location in the route
409                        boolean foundDes = false;
410                        for (RouteLocation rlocation : routeSequence) {
411                            if (train.isTrainEnRoute() && !foundTrainLoc) {
412                                if (train.getCurrentRouteLocation() != rlocation) {
413                                    continue;
414                                }
415                                foundTrainLoc = true;
416                            }
417                            if (rs.getLocationName().equals(rlocation.getName())) {
418                                rl = rlocation;
419                                foundLoc = true;
420                            }
421                            if (rs.getDestinationName().equals(rlocation.getName()) && foundLoc) {
422                                rd = rlocation;
423                                foundDes = true;
424                                if (rs.getDestinationTrack() != null &&
425                                        (rlocation.getTrainDirection() &
426                                                rs.getDestinationTrack().getTrainDirections()) == 0) {
427                                    continue; // destination track isn't serviced by the train's direction
428                                }
429                                break;
430                            }
431                        }
432                        if (!foundLoc) {
433                            JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
434                                    "rsTrainEnRoute"),
435                                    new Object[]{rs.toString(), train.getName(),
436                                            rs.getLocationName()}),
437                                    getRb().getString("rsNotMove"),
438                                    JmriJOptionPane.ERROR_MESSAGE);
439                            // prevent rs from being picked up and delivered
440                            setRouteLocationAndDestination(rs, train, null, null);
441                            return false;
442                        }
443                        if (!foundDes) {
444                            JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
445                                    "rsLocOrder"),
446                                    new Object[]{rs.getDestinationName(),
447                                            rs.getLocationName(), train.getName()}),
448                                    getRb().getString("rsNotMove"),
449                                    JmriJOptionPane.ERROR_MESSAGE);
450                            // prevent rs from being picked up and delivered
451                            setRouteLocationAndDestination(rs, train, null, null);
452                            return false;
453                        }
454                    }
455                }
456            }
457        }
458        return true;
459    }
460
461    protected boolean changeLocation(RollingStock rs) {
462        if (!ignoreLocationCheckBox.isSelected()) {
463            if (locationBox.getSelectedItem() == null) {
464                rs.setLocation(null, null);
465            } else {
466                if (trackLocationBox.getSelectedItem() == null) {
467                    JmriJOptionPane.showMessageDialog(this, getRb().getString("rsFullySelect"), getRb()
468                            .getString("rsCanNotLoc"), JmriJOptionPane.ERROR_MESSAGE);
469                    return false;
470                }
471                // update location only if it has changed
472                if (rs.getLocation() == null ||
473                        !rs.getLocation().equals(locationBox.getSelectedItem()) ||
474                        rs.getTrack() == null ||
475                        !rs.getTrack().equals(trackLocationBox.getSelectedItem())) {
476                    String status = rs.setLocation((Location) locationBox.getSelectedItem(),
477                            (Track) trackLocationBox.getSelectedItem());
478                    rs.setLastRouteId(RollingStock.NONE); // clear last route id
479                    rs.setLastTrain(null); // clear last train
480                    if (!status.equals(Track.OKAY)) {
481                        log.debug("Can't set rs's location because of {}", status);
482                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
483                                "rsCanNotLocMsg"), new Object[]{rs.toString(), status}), getRb()
484                                        .getString("rsCanNotLoc"),
485                                JmriJOptionPane.ERROR_MESSAGE);
486                        // does the user want to force the rolling stock to this track?
487                        int results = JmriJOptionPane.showOptionDialog(this, MessageFormat.format(getRb()
488                                .getString("rsForce"),
489                                new Object[]{rs.toString(),
490                                        (Track) trackLocationBox.getSelectedItem()}),
491                                MessageFormat.format(getRb()
492                                        .getString("rsOverride"), new Object[]{status}),
493                                JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE, null, null, null);
494                        if (results == JmriJOptionPane.YES_OPTION) {
495                            log.debug("Force rolling stock to track");
496                            rs.setLocation((Location) locationBox.getSelectedItem(), (Track) trackLocationBox
497                                    .getSelectedItem(), RollingStock.FORCE);
498                        } else {
499                            return false;
500                        }
501                    }
502                }
503            }
504        }
505        return true;
506    }
507
508    private void loadTrain(RollingStock rs) {
509        if (!ignoreTrainCheckBox.isSelected()) {
510            if (trainBox.getSelectedItem() == null) {
511                if (rs.getTrain() != null) {
512                    // prevent rs from being picked up and delivered
513                    setRouteLocationAndDestination(rs, rs.getTrain(), null, null);
514                }
515                rs.setTrain(null);
516            } else {
517                Train train = (Train) trainBox.getSelectedItem();
518                if (rs.getTrain() != null && !rs.getTrain().equals(train)) {
519                    // prevent rs from being picked up and delivered
520                    setRouteLocationAndDestination(rs, rs.getTrain(), null, null);
521                }
522                rs.setTrain(train);
523            }
524        }
525    }
526
527    private boolean changeDestination(RollingStock rs) {
528        if (!ignoreDestinationCheckBox.isSelected()) {
529            if (destinationBox.getSelectedItem() == null) {
530                rs.setDestination(null, null);
531            } else {
532                Track destTrack = null;
533                if (trackDestinationBox.getSelectedItem() != null) {
534                    destTrack = (Track) trackDestinationBox.getSelectedItem();
535                }
536                log.debug("changeDestination: {}, ({})", destinationBox.getSelectedItem(),
537                        destTrack);
538                if (destTrack != null &&
539                        rs.getDestinationTrack() != destTrack &&
540                        destTrack.isStaging() &&
541                        (rs.getTrain() == null || !rs.getTrain().isBuilt())) {
542                    log.debug("Destination track ({}) is staging", destTrack.getName());
543                    JmriJOptionPane.showMessageDialog(this, getRb().getString("rsDoNotSelectStaging"), getRb()
544                            .getString("rsCanNotDest"), JmriJOptionPane.ERROR_MESSAGE);
545                    return false;
546                }
547                // determine is user changed the destination track and is part of train
548                if (destTrack != null &&
549                        rs.getDestinationTrack() != destTrack &&
550                        rs.getTrain() != null &&
551                        rs.getTrain().isBuilt() &&
552                        rs.getRouteLocation() != null) {
553                    log.debug("Rolling stock ({}) has new track destination in built train ({})",
554                            rs.toString(), rs.getTrainName());
555                    rs.getTrain().setModified(true);
556                }
557                String status = rs.setDestination((Location) destinationBox.getSelectedItem(), destTrack);
558                if (!status.equals(Track.OKAY)) {
559                    log.debug("Can't set rs's destination because of {}", status);
560                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
561                            "rsCanNotDestMsg"), new Object[]{rs.toString(), status}), getRb().getString(
562                                    "rsCanNotDest"),
563                            JmriJOptionPane.ERROR_MESSAGE);
564                    return false;
565                }
566            }
567        }
568        return true;
569    }
570
571    /*
572     * Checks to see if rolling stock's location or destination has changed, and
573     * if so, removes the rolling stock from the train. Also allows user to add
574     * or remove rolling stock to or from train.
575     */
576    protected void checkTrain(RollingStock rs) {
577        Train train = rs.getTrain();
578        if (train != null && train.isBuilt()) {
579            if (rs.getRouteLocation() != null &&
580                    rs.getRouteDestination() != null &&
581                    rs.getTrack() == null) {
582                // no track
583                setRouteLocationAndDestination(rs, train, null, null);
584            }
585            if (rs.getRouteLocation() != null &&
586                    rs.getRouteDestination() != null &&
587                    rl != null &&
588                    rd != null &&
589                    (!rs.getRouteLocation().getName().equals(rl.getName()) ||
590                            !rs.getRouteDestination().getName().equals(rd.getName()))) {
591                // user changed rolling stock location or destination
592                setRouteLocationAndDestination(rs, train, null, null);
593            }
594            if (rs.getRouteLocation() != null || rs.getRouteDestination() != null) {
595                if (JmriJOptionPane.showConfirmDialog(this, MessageFormat.format(getRb().getString(
596                        "rsRemoveRsFromTrain"), new Object[]{rs.toString(), train.getName()}), getRb()
597                                .getString("rsInRoute"),
598                        JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
599                    // prevent rs from being picked up and delivered
600                    setRouteLocationAndDestination(rs, train, null, null);
601                }
602            } else if (rl != null && rd != null && rs.getDestinationTrack() != null) {
603                if (rs.getDestinationTrack().getLocation().isStaging() &&
604                        !rs.getDestinationTrack().equals(train.getTerminationTrack())) {
605                    log.debug("Rolling stock destination track is staging and not the same as train");
606                    JmriJOptionPane.showMessageDialog(this,
607                            Bundle.getMessage("rsMustSelectSameTrack", train.getTerminationTrack()
608                                    .getName()),
609                            Bundle.getMessage("rsStagingTrackError"), JmriJOptionPane.ERROR_MESSAGE);
610                } else if (JmriJOptionPane.showConfirmDialog(this, MessageFormat.format(
611                        getRb().getString("rsAddRsToTrain"), new Object[]{rs.toString(), train.getName()}),
612                        getRb().getString("rsAddManuallyToTrain"),
613                        JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
614                    // set new pick up and set out locations
615                    setRouteLocationAndDestination(rs, train, rl, rd);
616                    log.debug("Add rolling stock ({}) to train ({}) route pick up {} drop {}", rs.toString(), train
617                            .getName(), rl.getId(), rd.getId()); // NOI18N
618                }
619            }
620        }
621    }
622
623    protected void setRouteLocationAndDestination(RollingStock rs, Train train, RouteLocation rl,
624            RouteLocation rd) {
625        if (rs.getRouteLocation() != null || rl != null) {
626            train.setModified(true);
627        }
628        // check destination track is staging
629        if (rl == null &&
630                rd == null &&
631                rs.getDestinationTrack() != null &&
632                rs.getDestinationTrack().getLocation().isStaging()) {
633            log.debug("Rolling stock destination track is staging");
634            rs.setDestination(null, null);
635        }
636        rs.setRouteLocation(rl);
637        rs.setRouteDestination(rd);
638    }
639
640    protected void updateComboBoxes() {
641        log.debug("update combo boxes");
642        locationManager.updateComboBox(locationBox);
643        locationManager.updateComboBox(destinationBox);
644        locationManager.updateComboBox(finalDestinationBox);
645
646        updateLocationComboBoxes();
647        updateDestinationComboBoxes();
648    }
649
650    protected boolean updateGroup(List<T> list) {
651        for (RollingStock rs : list) {
652            if (rs == _rs) {
653                continue;
654            }
655            // Location status and out of service
656            if (!ignoreStatusCheckBox.isSelected()) {
657                rs.setLocationUnknown(locationUnknownCheckBox.isSelected());
658                rs.setOutOfService(outOfServiceCheckBox.isSelected());
659            }
660            // update location and destination
661            if (!changeLocation(rs)) {
662                return false;
663            }
664            if (!changeDestination(rs)) {
665                return false;
666            }
667
668            if (!ignoreTrainCheckBox.isSelected()) {
669                if (trainBox.getSelectedItem() == null) {
670                    rs.setTrain(null);
671                } else {
672                    rs.setTrain((Train) trainBox.getSelectedItem());
673                }
674            }
675            // set the route location and destination to match
676            rs.setRouteLocation(_rs.getRouteLocation());
677            rs.setRouteDestination(_rs.getRouteDestination());
678        }
679        return true;
680    }
681
682    @Override
683    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
684        log.debug("checkbox action ");
685        if (ae.getSource() == locationUnknownCheckBox) {
686            outOfServiceCheckBox.setSelected(locationUnknownCheckBox.isSelected());
687            enableComponents(!locationUnknownCheckBox.isSelected());
688        }
689        if (ae.getSource() == autoTrackCheckBox) {
690            updateLocationTrackComboBox();
691        }
692        if (ae.getSource() == autoDestinationTrackCheckBox) {
693            updateDestinationTrackComboBox();
694        }
695        if (ae.getSource() == ignoreStatusCheckBox) {
696            locationUnknownCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
697            outOfServiceCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
698        }
699        if (ae.getSource() == ignoreLocationCheckBox) {
700            locationBox.setEnabled(!ignoreLocationCheckBox.isSelected());
701            trackLocationBox.setEnabled(!ignoreLocationCheckBox.isSelected());
702            autoTrackCheckBox.setEnabled(!ignoreLocationCheckBox.isSelected());
703        }
704        if (ae.getSource() == ignoreDestinationCheckBox) {
705            destinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
706            trackDestinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
707            autoDestinationTrackCheckBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
708        }
709        if (ae.getSource() == ignoreFinalDestinationCheckBox) {
710            finalDestinationBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
711            finalDestTrackBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
712            autoFinalDestTrackCheckBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
713        }
714        if (ae.getSource() == ignoreTrainCheckBox) {
715            trainBox.setEnabled(!ignoreTrainCheckBox.isSelected());
716            autoTrainCheckBox.setEnabled(!ignoreTrainCheckBox.isSelected());
717        }
718    }
719
720    protected void enableComponents(boolean enabled) {
721        // combo boxes
722        locationBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
723        trackLocationBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
724        destinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
725        trackDestinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
726        finalDestinationBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
727        finalDestTrackBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
728        trainBox.setEnabled(!ignoreTrainCheckBox.isSelected() & enabled);
729        // checkboxes
730        autoTrackCheckBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
731        autoDestinationTrackCheckBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
732        autoFinalDestTrackCheckBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
733        autoTrainCheckBox.setEnabled(!ignoreTrainCheckBox.isSelected() & enabled);
734        locationUnknownCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
735        outOfServiceCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected() & enabled);
736
737        ignoreStatusCheckBox.setEnabled(enabled);
738        ignoreLocationCheckBox.setEnabled(enabled);
739        ignoreDestinationCheckBox.setEnabled(enabled);
740        ignoreFinalDestinationCheckBox.setEnabled(Setup.isCarRoutingEnabled() & enabled);
741        ignoreTrainCheckBox.setEnabled(enabled);
742    }
743
744    // location combo box
745    @Override
746    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
747        if (ae.getSource() == locationBox) {
748            updateLocationTrackComboBox();
749        }
750        if (ae.getSource() == destinationBox || ae.getSource() == trainBox) {
751            updateDestinationTrackComboBox();
752        }
753    }
754
755    protected void updateLocationComboBoxes() {
756        log.debug("update location combo boxes");
757        if (_rs != null) {
758            locationBox.setSelectedItem(_rs.getLocation());
759        }
760        // now update track combo boxes
761        updateLocationTrackComboBox();
762    }
763
764    protected void updateLocationTrackComboBox() {
765        log.debug("update location track combobox");
766        if (locationBox.getSelectedItem() == null) {
767            trackLocationBox.removeAllItems();
768        } else {
769            log.debug("RollingStockFrame sees location: {}", locationBox.getSelectedItem());
770            Location l = (Location) locationBox.getSelectedItem();
771            l.updateComboBox(trackLocationBox, _rs, autoTrackCheckBox.isSelected(), false);
772            if (_rs != null && _rs.getLocation() != null && _rs.getLocation().equals(l) && _rs.getTrack() != null) {
773                trackLocationBox.setSelectedItem(_rs.getTrack());
774            }
775        }
776    }
777
778    protected void updateDestinationComboBoxes() {
779        log.debug("update destination combo boxes");
780        if (_rs != null) {
781            destinationBox.setSelectedItem(_rs.getDestination());
782        }
783        // now update track combo boxes
784        updateDestinationTrackComboBox();
785    }
786
787    protected void updateDestinationTrackComboBox() {
788        log.debug("update destination track combobox");
789        if (destinationBox.getSelectedItem() == null) {
790            trackDestinationBox.removeAllItems();
791        } else {
792            log.debug("updateDestinationTrackComboBox destination: {}, ({})", destinationBox.getSelectedItem(),
793                    trackDestinationBox.getSelectedItem());
794            Location destination = (Location) destinationBox.getSelectedItem();
795            Track track = null;
796            if (trackDestinationBox.getSelectedItem() != null) {
797                track = (Track) trackDestinationBox.getSelectedItem();
798            }
799            destination.updateComboBox(trackDestinationBox, _rs, autoDestinationTrackCheckBox.isSelected(), true);
800            // check for staging, add track if train is built and terminates into staging
801            if (autoDestinationTrackCheckBox.isSelected() && trainBox.getSelectedItem() != null) {
802                Train train = (Train) trainBox.getSelectedItem();
803                if (train.isBuilt() &&
804                        train.getTerminationTrack() != null &&
805                        train.getTerminationTrack().getLocation() == destination) {
806                    trackDestinationBox.addItem(train.getTerminationTrack());
807                    trackDestinationBox.setSelectedItem(track);
808                }
809            }
810            if (_rs != null &&
811                    _rs.getDestination() != null &&
812                    _rs.getDestination().equals(destination) &&
813                    _rs.getDestinationTrack() != null) {
814                trackDestinationBox.setSelectedItem(_rs.getDestinationTrack());
815            } else if (track != null) {
816                trackDestinationBox.setSelectedItem(track);
817            }
818        }
819    }
820
821    protected void updateTrainComboBox() {
822        log.debug("update train combo box");
823        trainManager.updateTrainComboBox(trainBox);
824        if (_rs != null) {
825            trainBox.setSelectedItem(_rs.getTrain());
826        }
827    }
828
829    @Override
830    public void dispose() {
831        if (_rs != null) {
832            _rs.removePropertyChangeListener(this);
833        }
834        locationManager.removePropertyChangeListener(this);
835        trainManager.removePropertyChangeListener(this);
836        super.dispose();
837    }
838
839    @Override
840    public void propertyChange(java.beans.PropertyChangeEvent e) {
841        log.debug("PropertyChange ({}) new: ({})", e.getPropertyName(), e.getNewValue());
842        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) {
843            updateComboBoxes();
844        }
845        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY)) {
846            updateTrainComboBox();
847        }
848        if (e.getPropertyName().equals(RollingStock.TRACK_CHANGED_PROPERTY)) {
849            updateLocationComboBoxes();
850        }
851        if (e.getPropertyName().equals(RollingStock.DESTINATION_TRACK_CHANGED_PROPERTY)) {
852            updateDestinationComboBoxes();
853        }
854        if (e.getPropertyName().equals(RollingStock.TRAIN_CHANGED_PROPERTY)) {
855            if (_rs != null) {
856                trainBox.setSelectedItem(_rs.getTrain());
857            }
858        }
859    }
860
861    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RollingStockSetFrame.class);
862}