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