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.getRouteLocation() != null || _rs.getRouteDestination() != null) {
248            if (_rs.getRouteLocation() != null) {
249                log.debug("rs ({}) has a pick up location ({})", _rs.toString(), _rs.getRouteLocation().getName());
250            }
251            if (_rs.getRouteDestination() != null) {
252                log.debug("rs ({}) has a destination ({})", _rs.toString(), _rs.getRouteDestination().getName());
253            }
254            if (getClass() == CarsSetFrame.class || getClass() == EnginesSetFrame.class) {
255                JmriJOptionPane.showMessageDialog(this, getRb().getString("rsPressChangeWill"), getRb().getString(
256                        "rsInRoute"), JmriJOptionPane.WARNING_MESSAGE);
257            } else {
258                JmriJOptionPane.showMessageDialog(this, getRb().getString("rsPressSaveWill"), getRb().getString(
259                        "rsInRoute"), JmriJOptionPane.WARNING_MESSAGE);
260            }
261        }
262        _rs.addPropertyChangeListener(this);
263    }
264
265    // Save button
266    @Override
267    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
268        if (ae.getSource() == saveButton) {
269            if (save() && Setup.isCloseWindowOnSaveEnabled()) {
270                dispose();
271            }
272        }
273    }
274
275    abstract protected ResourceBundle getRb();
276
277    protected boolean save() {
278        return change(_rs);
279    }
280
281    // change(RollingStock rs) will load the route location and the route destination if possible
282    RouteLocation rl;
283    RouteLocation rd;
284
285    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification = "GUI ease of use")
286    protected boolean change(RollingStock rs) {
287        log.debug("Change button action for rs ({})", rs.toString());
288        // save the auto buttons
289        autoTrackCheckBoxSelected = autoTrackCheckBox.isSelected();
290        autoDestinationTrackCheckBoxSelected = autoDestinationTrackCheckBox.isSelected();
291        autoFinalDestTrackCheckBoxSelected = autoFinalDestTrackCheckBox.isSelected();
292        autoTrainCheckBoxSelected = autoTrainCheckBox.isSelected();
293
294        // save the statuses
295        if (!ignoreStatusCheckBox.isSelected()) {
296            rs.setLocationUnknown(locationUnknownCheckBox.isSelected());
297            rs.setOutOfService(outOfServiceCheckBox.isSelected());
298        }
299        // update location
300        if (!changeLocation(rs)) {
301            return false;
302        }
303        // check to see if rolling stock is in staging and out of service (also location unknown)
304        if (outOfServiceCheckBox.isSelected() && rs.getTrack() != null && rs.getTrack().isStaging()) {
305            JmriJOptionPane.showMessageDialog(this, getRb().getString("rsNeedToRemoveStaging"), getRb()
306                    .getString("rsInStaging"), JmriJOptionPane.WARNING_MESSAGE);
307            // clear the rolling stock's location
308            rs.setLocation(null, null);
309        }
310
311        loadTrain(rs);
312
313        // update destination
314        if (!changeDestination(rs)) {
315            return false;
316        }
317
318        updateTrainComboBox();
319
320        // check if train can service this rolling stock
321        if (!ignoreTrainCheckBox.isSelected()) {
322            Train train = rs.getTrain();
323            if (train != null) {
324                // determine if train services this rs's type
325                if (!train.isTypeNameAccepted(rs.getTypeName())) {
326                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
327                            "rsTrainNotServType"), new Object[]{rs.getTypeName(), train.getName()}), getRb()
328                                    .getString("rsNotMove"),
329                            JmriJOptionPane.ERROR_MESSAGE);
330                    // prevent rs from being picked up and delivered
331                    setRouteLocationAndDestination(rs, train, null, null);
332                    return false;
333                }
334                // determine if train services this rs's road
335                if (rs.getClass() == Car.class) {
336                    Car car = (Car) rs;
337                    if (!car.isCaboose() && !train.isCarRoadNameAccepted(car.getRoadName()) ||
338                            car.isCaboose() && !train.isCabooseRoadNameAccepted(car.getRoadName())) {
339                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
340                                "rsTrainNotServRoad"), new Object[]{rs.getRoadName(), train.getName()}), getRb()
341                                        .getString("rsNotMove"),
342                                JmriJOptionPane.ERROR_MESSAGE);
343                        // prevent rs from being picked up and delivered
344                        setRouteLocationAndDestination(rs, train, null, null);
345                        return false;
346                    }
347                } else if (!train.isLocoRoadNameAccepted(rs.getRoadName())) {
348                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
349                            "rsTrainNotServRoad"), new Object[]{rs.getRoadName(), train.getName()}), getRb()
350                                    .getString("rsNotMove"),
351                            JmriJOptionPane.ERROR_MESSAGE);
352                    // prevent rs from being picked up and delivered
353                    setRouteLocationAndDestination(rs, train, null, null);
354                    return false;
355                }
356                // determine if train services this rs's built date
357                if (!train.isBuiltDateAccepted(rs.getBuilt())) {
358                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
359                            "rsTrainNotServBuilt"), new Object[]{rs.getBuilt(), train.getName()}), getRb()
360                                    .getString("rsNotMove"),
361                            JmriJOptionPane.ERROR_MESSAGE);
362                    // prevent rs from being picked up and delivered
363                    setRouteLocationAndDestination(rs, train, null, null);
364                    return false;
365                }
366                // determine if train services this rs's owner
367                if (!train.isOwnerNameAccepted(rs.getOwnerName())) {
368                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
369                            "rsTrainNotServOwner"), new Object[]{rs.getOwnerName(), train.getName()}), getRb()
370                                    .getString("rsNotMove"),
371                            JmriJOptionPane.ERROR_MESSAGE);
372                    // prevent rs from being picked up and delivered
373                    setRouteLocationAndDestination(rs, train, null, null);
374                    return false;
375                }
376                // determine if train services the location and destination selected by user
377                rl = null;
378                rd = null;
379                if (rs.getLocation() != null) {
380                    Route route = train.getRoute();
381                    if (route != null) {
382                        // this is a quick check, the actual rl and rd are set later in this routine.
383                        rl = route.getLastLocationByName(rs.getLocationName());
384                        rd = route.getLastLocationByName(rs.getDestinationName());
385                    }
386                    if (rl == null) {
387                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
388                                "rsLocNotServ"), new Object[]{rs.getLocationName(), train.getName()}),
389                                getRb().getString("rsNotMove"), JmriJOptionPane.ERROR_MESSAGE);
390                        // prevent rs from being picked up and delivered
391                        setRouteLocationAndDestination(rs, train, null, null);
392                        return false;
393                    }
394                    if (rd == null && !rs.getDestinationName().equals(RollingStock.NONE)) {
395                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
396                                "rsDestNotServ"), new Object[]{rs.getDestinationName(), train.getName()}),
397                                getRb().getString("rsNotMove"), JmriJOptionPane.ERROR_MESSAGE);
398                        // prevent rs from being picked up and delivered
399                        setRouteLocationAndDestination(rs, train, null, null);
400                        return false;
401                    }
402                    if (rd != null && route != null) {
403                        // now determine if destination is after location
404                        List<RouteLocation> routeSequence = route.getLocationsBySequenceList();
405                        boolean foundTrainLoc = false; // when true, found the train's location
406                        boolean foundLoc = false; // when true, found the rs's location in the route
407                        boolean foundDes = false;
408                        for (RouteLocation rlocation : routeSequence) {
409                            if (train.isTrainEnRoute() && !foundTrainLoc) {
410                                if (train.getCurrentRouteLocation() != rlocation) {
411                                    continue;
412                                }
413                                foundTrainLoc = true;
414                            }
415                            if (rs.getLocationName().equals(rlocation.getName())) {
416                                rl = rlocation;
417                                foundLoc = true;
418                            }
419                            if (rs.getDestinationName().equals(rlocation.getName()) && foundLoc) {
420                                rd = rlocation;
421                                foundDes = true;
422                                if (rs.getDestinationTrack() != null &&
423                                        (rlocation.getTrainDirection() &
424                                                rs.getDestinationTrack().getTrainDirections()) == 0) {
425                                    continue; // destination track isn't serviced by the train's direction
426                                }
427                                break;
428                            }
429                        }
430                        if (!foundLoc) {
431                            JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
432                                    "rsTrainEnRoute"),
433                                    new Object[]{rs.toString(), train.getName(),
434                                            rs.getLocationName()}),
435                                    getRb().getString("rsNotMove"),
436                                    JmriJOptionPane.ERROR_MESSAGE);
437                            // prevent rs from being picked up and delivered
438                            setRouteLocationAndDestination(rs, train, null, null);
439                            return false;
440                        }
441                        if (!foundDes) {
442                            JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
443                                    "rsLocOrder"),
444                                    new Object[]{rs.getDestinationName(),
445                                            rs.getLocationName(), train.getName()}),
446                                    getRb().getString("rsNotMove"),
447                                    JmriJOptionPane.ERROR_MESSAGE);
448                            // prevent rs from being picked up and delivered
449                            setRouteLocationAndDestination(rs, train, null, null);
450                            return false;
451                        }
452                    }
453                }
454            }
455        }
456        return true;
457    }
458
459    protected boolean changeLocation(RollingStock rs) {
460        if (!ignoreLocationCheckBox.isSelected()) {
461            if (locationBox.getSelectedItem() == null) {
462                rs.setLocation(null, null);
463            } else {
464                if (trackLocationBox.getSelectedItem() == null) {
465                    JmriJOptionPane.showMessageDialog(this, getRb().getString("rsFullySelect"), getRb()
466                            .getString("rsCanNotLoc"), JmriJOptionPane.ERROR_MESSAGE);
467                    return false;
468                }
469                // update location only if it has changed
470                if (rs.getLocation() == null ||
471                        !rs.getLocation().equals(locationBox.getSelectedItem()) ||
472                        rs.getTrack() == null ||
473                        !rs.getTrack().equals(trackLocationBox.getSelectedItem())) {
474                    String status = rs.setLocation((Location) locationBox.getSelectedItem(),
475                            (Track) trackLocationBox.getSelectedItem());
476                    rs.setLastRouteId(RollingStock.NONE); // clear last route id
477                    if (!status.equals(Track.OKAY)) {
478                        log.debug("Can't set rs's location because of {}", status);
479                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
480                                "rsCanNotLocMsg"), new Object[]{rs.toString(), status}), getRb()
481                                        .getString("rsCanNotLoc"),
482                                JmriJOptionPane.ERROR_MESSAGE);
483                        // does the user want to force the rolling stock to this track?
484                        int results = JmriJOptionPane.showOptionDialog(this, MessageFormat.format(getRb()
485                                .getString("rsForce"),
486                                new Object[]{rs.toString(),
487                                        (Track) trackLocationBox.getSelectedItem()}),
488                                MessageFormat.format(getRb()
489                                        .getString("rsOverride"), new Object[]{status}),
490                                JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE, null, null, null);
491                        if (results == JmriJOptionPane.YES_OPTION) {
492                            log.debug("Force rolling stock to track");
493                            rs.setLocation((Location) locationBox.getSelectedItem(), (Track) trackLocationBox
494                                    .getSelectedItem(), RollingStock.FORCE);
495                        } else {
496                            return false;
497                        }
498                    }
499                }
500            }
501        }
502        return true;
503    }
504
505    private void loadTrain(RollingStock rs) {
506        if (!ignoreTrainCheckBox.isSelected()) {
507            if (trainBox.getSelectedItem() == null) {
508                if (rs.getTrain() != null) {
509                    // prevent rs from being picked up and delivered
510                    setRouteLocationAndDestination(rs, rs.getTrain(), null, null);
511                }
512                rs.setTrain(null);
513            } else {
514                Train train = (Train) trainBox.getSelectedItem();
515                if (rs.getTrain() != null && !rs.getTrain().equals(train)) {
516                    // prevent rs from being picked up and delivered
517                    setRouteLocationAndDestination(rs, rs.getTrain(), null, null);
518                }
519                rs.setTrain(train);
520            }
521        }
522    }
523
524    private boolean changeDestination(RollingStock rs) {
525        if (!ignoreDestinationCheckBox.isSelected()) {
526            if (destinationBox.getSelectedItem() == null) {
527                rs.setDestination(null, null);
528            } else {
529                Track destTrack = null;
530                if (trackDestinationBox.getSelectedItem() != null) {
531                    destTrack = (Track) trackDestinationBox.getSelectedItem();
532                }
533                log.debug("changeDestination: {}, ({})", destinationBox.getSelectedItem(),
534                        destTrack);
535                if (destTrack != null &&
536                        rs.getDestinationTrack() != destTrack &&
537                        destTrack.isStaging() &&
538                        (rs.getTrain() == null || !rs.getTrain().isBuilt())) {
539                    log.debug("Destination track ({}) is staging", destTrack.getName());
540                    JmriJOptionPane.showMessageDialog(this, getRb().getString("rsDoNotSelectStaging"), getRb()
541                            .getString("rsCanNotDest"), JmriJOptionPane.ERROR_MESSAGE);
542                    return false;
543                }
544                // determine is user changed the destination track and is part of train
545                if (destTrack != null &&
546                        rs.getDestinationTrack() != destTrack &&
547                        rs.getTrain() != null &&
548                        rs.getTrain().isBuilt() &&
549                        rs.getRouteLocation() != null) {
550                    log.debug("Rolling stock ({}) has new track destination in built train ({})",
551                            rs.toString(), rs.getTrainName());
552                    rs.getTrain().setModified(true);
553                }
554                String status = rs.setDestination((Location) destinationBox.getSelectedItem(), destTrack);
555                if (!status.equals(Track.OKAY)) {
556                    log.debug("Can't set rs's destination because of {}", status);
557                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
558                            "rsCanNotDestMsg"), new Object[]{rs.toString(), status}), getRb().getString(
559                                    "rsCanNotDest"),
560                            JmriJOptionPane.ERROR_MESSAGE);
561                    return false;
562                }
563            }
564        }
565        return true;
566    }
567
568    /*
569     * Checks to see if rolling stock's location or destination has changed, and
570     * if so, removes the rolling stock from the train. Also allows user to add
571     * or remove rolling stock to or from train.
572     */
573    protected void checkTrain(RollingStock rs) {
574        Train train = rs.getTrain();
575        if (train != null && train.isBuilt()) {
576            if (rs.getRouteLocation() != null &&
577                    rs.getRouteDestination() != null &&
578                    rs.getTrack() == null) {
579                // no track
580                setRouteLocationAndDestination(rs, train, null, null);
581            }
582            if (rs.getRouteLocation() != null &&
583                    rs.getRouteDestination() != null &&
584                    rl != null &&
585                    rd != null &&
586                    (!rs.getRouteLocation().getName().equals(rl.getName()) ||
587                            !rs.getRouteDestination().getName().equals(rd.getName()))) {
588                // user changed rolling stock location or destination
589                setRouteLocationAndDestination(rs, train, null, null);
590            }
591            if (rs.getRouteLocation() != null || rs.getRouteDestination() != null) {
592                if (JmriJOptionPane.showConfirmDialog(this, MessageFormat.format(getRb().getString(
593                        "rsRemoveRsFromTrain"), new Object[]{rs.toString(), train.getName()}), getRb()
594                                .getString("rsInRoute"),
595                        JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
596                    // prevent rs from being picked up and delivered
597                    setRouteLocationAndDestination(rs, train, null, null);
598                }
599            } else if (rl != null && rd != null && rs.getDestinationTrack() != null) {
600                if (rs.getDestinationTrack().getLocation().isStaging() &&
601                        !rs.getDestinationTrack().equals(train.getTerminationTrack())) {
602                    log.debug("Rolling stock destination track is staging and not the same as train");
603                    JmriJOptionPane.showMessageDialog(this,
604                            Bundle.getMessage("rsMustSelectSameTrack", train.getTerminationTrack()
605                                    .getName()),
606                            Bundle.getMessage("rsStagingTrackError"), JmriJOptionPane.ERROR_MESSAGE);
607                } else if (JmriJOptionPane.showConfirmDialog(this, MessageFormat.format(
608                        getRb().getString("rsAddRsToTrain"), new Object[]{rs.toString(), train.getName()}),
609                        getRb().getString("rsAddManuallyToTrain"),
610                        JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
611                    // set new pick up and set out locations
612                    setRouteLocationAndDestination(rs, train, rl, rd);
613                    log.debug("Add rolling stock ({}) to train ({}) route pick up {} drop {}", rs.toString(), train
614                            .getName(), rl.getId(), rd.getId()); // NOI18N
615                }
616            }
617        }
618    }
619
620    protected void setRouteLocationAndDestination(RollingStock rs, Train train, RouteLocation rl,
621            RouteLocation rd) {
622        if (rs.getRouteLocation() != null || rl != null) {
623            train.setModified(true);
624        }
625        // check destination track is staging
626        if (rl == null &&
627                rd == null &&
628                rs.getDestinationTrack() != null &&
629                rs.getDestinationTrack().getLocation().isStaging()) {
630            log.debug("Rolling stock destination track is staging");
631            rs.setDestination(null, null);
632        }
633        rs.setRouteLocation(rl);
634        rs.setRouteDestination(rd);
635    }
636
637    protected void updateComboBoxes() {
638        log.debug("update combo boxes");
639        locationManager.updateComboBox(locationBox);
640        locationManager.updateComboBox(destinationBox);
641        locationManager.updateComboBox(finalDestinationBox);
642
643        updateLocationComboBoxes();
644        updateDestinationComboBoxes();
645    }
646
647    protected boolean updateGroup(List<T> list) {
648        for (RollingStock rs : list) {
649            if (rs == _rs) {
650                continue;
651            }
652            // Location status and out of service
653            if (!ignoreStatusCheckBox.isSelected()) {
654                rs.setLocationUnknown(locationUnknownCheckBox.isSelected());
655                rs.setOutOfService(outOfServiceCheckBox.isSelected());
656            }
657            // update location and destination
658            if (!changeLocation(rs)) {
659                return false;
660            }
661            if (!changeDestination(rs)) {
662                return false;
663            }
664
665            if (!ignoreTrainCheckBox.isSelected()) {
666                if (trainBox.getSelectedItem() == null) {
667                    rs.setTrain(null);
668                } else {
669                    rs.setTrain((Train) trainBox.getSelectedItem());
670                }
671            }
672            // set the route location and destination to match
673            rs.setRouteLocation(_rs.getRouteLocation());
674            rs.setRouteDestination(_rs.getRouteDestination());
675        }
676        return true;
677    }
678
679    @Override
680    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
681        log.debug("checkbox action ");
682        if (ae.getSource() == locationUnknownCheckBox) {
683            outOfServiceCheckBox.setSelected(locationUnknownCheckBox.isSelected());
684            enableComponents(!locationUnknownCheckBox.isSelected());
685        }
686        if (ae.getSource() == autoTrackCheckBox) {
687            updateLocationTrackComboBox();
688        }
689        if (ae.getSource() == autoDestinationTrackCheckBox) {
690            updateDestinationTrackComboBox();
691        }
692        if (ae.getSource() == ignoreStatusCheckBox) {
693            locationUnknownCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
694            outOfServiceCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
695        }
696        if (ae.getSource() == ignoreLocationCheckBox) {
697            locationBox.setEnabled(!ignoreLocationCheckBox.isSelected());
698            trackLocationBox.setEnabled(!ignoreLocationCheckBox.isSelected());
699            autoTrackCheckBox.setEnabled(!ignoreLocationCheckBox.isSelected());
700        }
701        if (ae.getSource() == ignoreDestinationCheckBox) {
702            destinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
703            trackDestinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
704            autoDestinationTrackCheckBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
705        }
706        if (ae.getSource() == ignoreFinalDestinationCheckBox) {
707            finalDestinationBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
708            finalDestTrackBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
709            autoFinalDestTrackCheckBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
710        }
711        if (ae.getSource() == ignoreTrainCheckBox) {
712            trainBox.setEnabled(!ignoreTrainCheckBox.isSelected());
713            autoTrainCheckBox.setEnabled(!ignoreTrainCheckBox.isSelected());
714        }
715    }
716
717    protected void enableComponents(boolean enabled) {
718        // combo boxes
719        locationBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
720        trackLocationBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
721        destinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
722        trackDestinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
723        finalDestinationBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
724        finalDestTrackBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
725        trainBox.setEnabled(!ignoreTrainCheckBox.isSelected() & enabled);
726        // checkboxes
727        autoTrackCheckBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
728        autoDestinationTrackCheckBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
729        autoFinalDestTrackCheckBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
730        autoTrainCheckBox.setEnabled(!ignoreTrainCheckBox.isSelected() & enabled);
731        locationUnknownCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
732        outOfServiceCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected() & enabled);
733
734        ignoreStatusCheckBox.setEnabled(enabled);
735        ignoreLocationCheckBox.setEnabled(enabled);
736        ignoreDestinationCheckBox.setEnabled(enabled);
737        ignoreFinalDestinationCheckBox.setEnabled(Setup.isCarRoutingEnabled() & enabled);
738        ignoreTrainCheckBox.setEnabled(enabled);
739    }
740
741    // location combo box
742    @Override
743    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
744        if (ae.getSource() == locationBox) {
745            updateLocationTrackComboBox();
746        }
747        if (ae.getSource() == destinationBox || ae.getSource() == trainBox) {
748            updateDestinationTrackComboBox();
749        }
750    }
751
752    protected void updateLocationComboBoxes() {
753        log.debug("update location combo boxes");
754        if (_rs != null) {
755            locationBox.setSelectedItem(_rs.getLocation());
756        }
757        // now update track combo boxes
758        updateLocationTrackComboBox();
759    }
760
761    protected void updateLocationTrackComboBox() {
762        log.debug("update location track combobox");
763        if (locationBox.getSelectedItem() == null) {
764            trackLocationBox.removeAllItems();
765        } else {
766            log.debug("RollingStockFrame sees location: {}", locationBox.getSelectedItem());
767            Location l = (Location) locationBox.getSelectedItem();
768            l.updateComboBox(trackLocationBox, _rs, autoTrackCheckBox.isSelected(), false);
769            if (_rs != null && _rs.getLocation() != null && _rs.getLocation().equals(l) && _rs.getTrack() != null) {
770                trackLocationBox.setSelectedItem(_rs.getTrack());
771            }
772        }
773    }
774
775    protected void updateDestinationComboBoxes() {
776        log.debug("update destination combo boxes");
777        if (_rs != null) {
778            destinationBox.setSelectedItem(_rs.getDestination());
779        }
780        // now update track combo boxes
781        updateDestinationTrackComboBox();
782    }
783
784    protected void updateDestinationTrackComboBox() {
785        log.debug("update destination track combobox");
786        if (destinationBox.getSelectedItem() == null) {
787            trackDestinationBox.removeAllItems();
788        } else {
789            log.debug("updateDestinationTrackComboBox destination: {}, ({})", destinationBox.getSelectedItem(),
790                    trackDestinationBox.getSelectedItem());
791            Location destination = (Location) destinationBox.getSelectedItem();
792            Track track = null;
793            if (trackDestinationBox.getSelectedItem() != null) {
794                track = (Track) trackDestinationBox.getSelectedItem();
795            }
796            destination.updateComboBox(trackDestinationBox, _rs, autoDestinationTrackCheckBox.isSelected(), true);
797            // check for staging, add track if train is built and terminates into staging
798            if (autoDestinationTrackCheckBox.isSelected() && trainBox.getSelectedItem() != null) {
799                Train train = (Train) trainBox.getSelectedItem();
800                if (train.isBuilt() &&
801                        train.getTerminationTrack() != null &&
802                        train.getTerminationTrack().getLocation() == destination) {
803                    trackDestinationBox.addItem(train.getTerminationTrack());
804                    trackDestinationBox.setSelectedItem(track);
805                }
806            }
807            if (_rs != null &&
808                    _rs.getDestination() != null &&
809                    _rs.getDestination().equals(destination) &&
810                    _rs.getDestinationTrack() != null) {
811                trackDestinationBox.setSelectedItem(_rs.getDestinationTrack());
812            } else if (track != null) {
813                trackDestinationBox.setSelectedItem(track);
814            }
815        }
816    }
817
818    protected void updateTrainComboBox() {
819        log.debug("update train combo box");
820        trainManager.updateTrainComboBox(trainBox);
821        if (_rs != null) {
822            trainBox.setSelectedItem(_rs.getTrain());
823        }
824    }
825
826    @Override
827    public void dispose() {
828        if (_rs != null) {
829            _rs.removePropertyChangeListener(this);
830        }
831        locationManager.removePropertyChangeListener(this);
832        trainManager.removePropertyChangeListener(this);
833        super.dispose();
834    }
835
836    @Override
837    public void propertyChange(java.beans.PropertyChangeEvent e) {
838        log.debug("PropertyChange ({}) new: ({})", e.getPropertyName(), e.getNewValue());
839        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) {
840            updateComboBoxes();
841        }
842        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY)) {
843            updateTrainComboBox();
844        }
845        if (e.getPropertyName().equals(RollingStock.TRACK_CHANGED_PROPERTY)) {
846            updateLocationComboBoxes();
847        }
848        if (e.getPropertyName().equals(RollingStock.DESTINATION_TRACK_CHANGED_PROPERTY)) {
849            updateDestinationComboBoxes();
850        }
851        if (e.getPropertyName().equals(RollingStock.TRAIN_CHANGED_PROPERTY)) {
852            if (_rs != null) {
853                trainBox.setSelectedItem(_rs.getTrain());
854            }
855        }
856    }
857
858    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RollingStockSetFrame.class);
859}