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                    if (!status.equals(Track.OKAY)) {
480                        log.debug("Can't set rs's location because of {}", status);
481                        JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
482                                "rsCanNotLocMsg"), new Object[]{rs.toString(), status}), getRb()
483                                        .getString("rsCanNotLoc"),
484                                JmriJOptionPane.ERROR_MESSAGE);
485                        // does the user want to force the rolling stock to this track?
486                        int results = JmriJOptionPane.showOptionDialog(this, MessageFormat.format(getRb()
487                                .getString("rsForce"),
488                                new Object[]{rs.toString(),
489                                        (Track) trackLocationBox.getSelectedItem()}),
490                                MessageFormat.format(getRb()
491                                        .getString("rsOverride"), new Object[]{status}),
492                                JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE, null, null, null);
493                        if (results == JmriJOptionPane.YES_OPTION) {
494                            log.debug("Force rolling stock to track");
495                            rs.setLocation((Location) locationBox.getSelectedItem(), (Track) trackLocationBox
496                                    .getSelectedItem(), RollingStock.FORCE);
497                        } else {
498                            return false;
499                        }
500                    }
501                }
502            }
503        }
504        return true;
505    }
506
507    private void loadTrain(RollingStock rs) {
508        if (!ignoreTrainCheckBox.isSelected()) {
509            if (trainBox.getSelectedItem() == null) {
510                if (rs.getTrain() != null) {
511                    // prevent rs from being picked up and delivered
512                    setRouteLocationAndDestination(rs, rs.getTrain(), null, null);
513                }
514                rs.setTrain(null);
515            } else {
516                Train train = (Train) trainBox.getSelectedItem();
517                if (rs.getTrain() != null && !rs.getTrain().equals(train)) {
518                    // prevent rs from being picked up and delivered
519                    setRouteLocationAndDestination(rs, rs.getTrain(), null, null);
520                }
521                rs.setTrain(train);
522            }
523        }
524    }
525
526    private boolean changeDestination(RollingStock rs) {
527        if (!ignoreDestinationCheckBox.isSelected()) {
528            if (destinationBox.getSelectedItem() == null) {
529                rs.setDestination(null, null);
530            } else {
531                Track destTrack = null;
532                if (trackDestinationBox.getSelectedItem() != null) {
533                    destTrack = (Track) trackDestinationBox.getSelectedItem();
534                }
535                log.debug("changeDestination: {}, ({})", destinationBox.getSelectedItem(),
536                        destTrack);
537                if (destTrack != null &&
538                        rs.getDestinationTrack() != destTrack &&
539                        destTrack.isStaging() &&
540                        (rs.getTrain() == null || !rs.getTrain().isBuilt())) {
541                    log.debug("Destination track ({}) is staging", destTrack.getName());
542                    JmriJOptionPane.showMessageDialog(this, getRb().getString("rsDoNotSelectStaging"), getRb()
543                            .getString("rsCanNotDest"), JmriJOptionPane.ERROR_MESSAGE);
544                    return false;
545                }
546                // determine is user changed the destination track and is part of train
547                if (destTrack != null &&
548                        rs.getDestinationTrack() != destTrack &&
549                        rs.getTrain() != null &&
550                        rs.getTrain().isBuilt() &&
551                        rs.getRouteLocation() != null) {
552                    log.debug("Rolling stock ({}) has new track destination in built train ({})",
553                            rs.toString(), rs.getTrainName());
554                    rs.getTrain().setModified(true);
555                }
556                String status = rs.setDestination((Location) destinationBox.getSelectedItem(), destTrack);
557                if (!status.equals(Track.OKAY)) {
558                    log.debug("Can't set rs's destination because of {}", status);
559                    JmriJOptionPane.showMessageDialog(this, MessageFormat.format(getRb().getString(
560                            "rsCanNotDestMsg"), new Object[]{rs.toString(), status}), getRb().getString(
561                                    "rsCanNotDest"),
562                            JmriJOptionPane.ERROR_MESSAGE);
563                    return false;
564                }
565            }
566        }
567        return true;
568    }
569
570    /*
571     * Checks to see if rolling stock's location or destination has changed, and
572     * if so, removes the rolling stock from the train. Also allows user to add
573     * or remove rolling stock to or from train.
574     */
575    protected void checkTrain(RollingStock rs) {
576        Train train = rs.getTrain();
577        if (train != null && train.isBuilt()) {
578            if (rs.getRouteLocation() != null &&
579                    rs.getRouteDestination() != null &&
580                    rs.getTrack() == null) {
581                // no track
582                setRouteLocationAndDestination(rs, train, null, null);
583            }
584            if (rs.getRouteLocation() != null &&
585                    rs.getRouteDestination() != null &&
586                    rl != null &&
587                    rd != null &&
588                    (!rs.getRouteLocation().getName().equals(rl.getName()) ||
589                            !rs.getRouteDestination().getName().equals(rd.getName()))) {
590                // user changed rolling stock location or destination
591                setRouteLocationAndDestination(rs, train, null, null);
592            }
593            if (rs.getRouteLocation() != null || rs.getRouteDestination() != null) {
594                if (JmriJOptionPane.showConfirmDialog(this, MessageFormat.format(getRb().getString(
595                        "rsRemoveRsFromTrain"), new Object[]{rs.toString(), train.getName()}), getRb()
596                                .getString("rsInRoute"),
597                        JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
598                    // prevent rs from being picked up and delivered
599                    setRouteLocationAndDestination(rs, train, null, null);
600                }
601            } else if (rl != null && rd != null && rs.getDestinationTrack() != null) {
602                if (rs.getDestinationTrack().getLocation().isStaging() &&
603                        !rs.getDestinationTrack().equals(train.getTerminationTrack())) {
604                    log.debug("Rolling stock destination track is staging and not the same as train");
605                    JmriJOptionPane.showMessageDialog(this,
606                            Bundle.getMessage("rsMustSelectSameTrack", train.getTerminationTrack()
607                                    .getName()),
608                            Bundle.getMessage("rsStagingTrackError"), JmriJOptionPane.ERROR_MESSAGE);
609                } else if (JmriJOptionPane.showConfirmDialog(this, MessageFormat.format(
610                        getRb().getString("rsAddRsToTrain"), new Object[]{rs.toString(), train.getName()}),
611                        getRb().getString("rsAddManuallyToTrain"),
612                        JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
613                    // set new pick up and set out locations
614                    setRouteLocationAndDestination(rs, train, rl, rd);
615                    log.debug("Add rolling stock ({}) to train ({}) route pick up {} drop {}", rs.toString(), train
616                            .getName(), rl.getId(), rd.getId()); // NOI18N
617                }
618            }
619        }
620    }
621
622    protected void setRouteLocationAndDestination(RollingStock rs, Train train, RouteLocation rl,
623            RouteLocation rd) {
624        if (rs.getRouteLocation() != null || rl != null) {
625            train.setModified(true);
626        }
627        // check destination track is staging
628        if (rl == null &&
629                rd == null &&
630                rs.getDestinationTrack() != null &&
631                rs.getDestinationTrack().getLocation().isStaging()) {
632            log.debug("Rolling stock destination track is staging");
633            rs.setDestination(null, null);
634        }
635        rs.setRouteLocation(rl);
636        rs.setRouteDestination(rd);
637    }
638
639    protected void updateComboBoxes() {
640        log.debug("update combo boxes");
641        locationManager.updateComboBox(locationBox);
642        locationManager.updateComboBox(destinationBox);
643        locationManager.updateComboBox(finalDestinationBox);
644
645        updateLocationComboBoxes();
646        updateDestinationComboBoxes();
647    }
648
649    protected boolean updateGroup(List<T> list) {
650        for (RollingStock rs : list) {
651            if (rs == _rs) {
652                continue;
653            }
654            // Location status and out of service
655            if (!ignoreStatusCheckBox.isSelected()) {
656                rs.setLocationUnknown(locationUnknownCheckBox.isSelected());
657                rs.setOutOfService(outOfServiceCheckBox.isSelected());
658            }
659            // update location and destination
660            if (!changeLocation(rs)) {
661                return false;
662            }
663            if (!changeDestination(rs)) {
664                return false;
665            }
666
667            if (!ignoreTrainCheckBox.isSelected()) {
668                if (trainBox.getSelectedItem() == null) {
669                    rs.setTrain(null);
670                } else {
671                    rs.setTrain((Train) trainBox.getSelectedItem());
672                }
673            }
674            // set the route location and destination to match
675            rs.setRouteLocation(_rs.getRouteLocation());
676            rs.setRouteDestination(_rs.getRouteDestination());
677        }
678        return true;
679    }
680
681    @Override
682    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
683        log.debug("checkbox action ");
684        if (ae.getSource() == locationUnknownCheckBox) {
685            outOfServiceCheckBox.setSelected(locationUnknownCheckBox.isSelected());
686            enableComponents(!locationUnknownCheckBox.isSelected());
687        }
688        if (ae.getSource() == autoTrackCheckBox) {
689            updateLocationTrackComboBox();
690        }
691        if (ae.getSource() == autoDestinationTrackCheckBox) {
692            updateDestinationTrackComboBox();
693        }
694        if (ae.getSource() == ignoreStatusCheckBox) {
695            locationUnknownCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
696            outOfServiceCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
697        }
698        if (ae.getSource() == ignoreLocationCheckBox) {
699            locationBox.setEnabled(!ignoreLocationCheckBox.isSelected());
700            trackLocationBox.setEnabled(!ignoreLocationCheckBox.isSelected());
701            autoTrackCheckBox.setEnabled(!ignoreLocationCheckBox.isSelected());
702        }
703        if (ae.getSource() == ignoreDestinationCheckBox) {
704            destinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
705            trackDestinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
706            autoDestinationTrackCheckBox.setEnabled(!ignoreDestinationCheckBox.isSelected());
707        }
708        if (ae.getSource() == ignoreFinalDestinationCheckBox) {
709            finalDestinationBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
710            finalDestTrackBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
711            autoFinalDestTrackCheckBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected());
712        }
713        if (ae.getSource() == ignoreTrainCheckBox) {
714            trainBox.setEnabled(!ignoreTrainCheckBox.isSelected());
715            autoTrainCheckBox.setEnabled(!ignoreTrainCheckBox.isSelected());
716        }
717    }
718
719    protected void enableComponents(boolean enabled) {
720        // combo boxes
721        locationBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
722        trackLocationBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
723        destinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
724        trackDestinationBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
725        finalDestinationBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
726        finalDestTrackBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
727        trainBox.setEnabled(!ignoreTrainCheckBox.isSelected() & enabled);
728        // checkboxes
729        autoTrackCheckBox.setEnabled(!ignoreLocationCheckBox.isSelected() & enabled);
730        autoDestinationTrackCheckBox.setEnabled(!ignoreDestinationCheckBox.isSelected() & enabled);
731        autoFinalDestTrackCheckBox.setEnabled(!ignoreFinalDestinationCheckBox.isSelected() & enabled);
732        autoTrainCheckBox.setEnabled(!ignoreTrainCheckBox.isSelected() & enabled);
733        locationUnknownCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected());
734        outOfServiceCheckBox.setEnabled(!ignoreStatusCheckBox.isSelected() & enabled);
735
736        ignoreStatusCheckBox.setEnabled(enabled);
737        ignoreLocationCheckBox.setEnabled(enabled);
738        ignoreDestinationCheckBox.setEnabled(enabled);
739        ignoreFinalDestinationCheckBox.setEnabled(Setup.isCarRoutingEnabled() & enabled);
740        ignoreTrainCheckBox.setEnabled(enabled);
741    }
742
743    // location combo box
744    @Override
745    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
746        if (ae.getSource() == locationBox) {
747            updateLocationTrackComboBox();
748        }
749        if (ae.getSource() == destinationBox || ae.getSource() == trainBox) {
750            updateDestinationTrackComboBox();
751        }
752    }
753
754    protected void updateLocationComboBoxes() {
755        log.debug("update location combo boxes");
756        if (_rs != null) {
757            locationBox.setSelectedItem(_rs.getLocation());
758        }
759        // now update track combo boxes
760        updateLocationTrackComboBox();
761    }
762
763    protected void updateLocationTrackComboBox() {
764        log.debug("update location track combobox");
765        if (locationBox.getSelectedItem() == null) {
766            trackLocationBox.removeAllItems();
767        } else {
768            log.debug("RollingStockFrame sees location: {}", locationBox.getSelectedItem());
769            Location l = (Location) locationBox.getSelectedItem();
770            l.updateComboBox(trackLocationBox, _rs, autoTrackCheckBox.isSelected(), false);
771            if (_rs != null && _rs.getLocation() != null && _rs.getLocation().equals(l) && _rs.getTrack() != null) {
772                trackLocationBox.setSelectedItem(_rs.getTrack());
773            }
774        }
775    }
776
777    protected void updateDestinationComboBoxes() {
778        log.debug("update destination combo boxes");
779        if (_rs != null) {
780            destinationBox.setSelectedItem(_rs.getDestination());
781        }
782        // now update track combo boxes
783        updateDestinationTrackComboBox();
784    }
785
786    protected void updateDestinationTrackComboBox() {
787        log.debug("update destination track combobox");
788        if (destinationBox.getSelectedItem() == null) {
789            trackDestinationBox.removeAllItems();
790        } else {
791            log.debug("updateDestinationTrackComboBox destination: {}, ({})", destinationBox.getSelectedItem(),
792                    trackDestinationBox.getSelectedItem());
793            Location destination = (Location) destinationBox.getSelectedItem();
794            Track track = null;
795            if (trackDestinationBox.getSelectedItem() != null) {
796                track = (Track) trackDestinationBox.getSelectedItem();
797            }
798            destination.updateComboBox(trackDestinationBox, _rs, autoDestinationTrackCheckBox.isSelected(), true);
799            // check for staging, add track if train is built and terminates into staging
800            if (autoDestinationTrackCheckBox.isSelected() && trainBox.getSelectedItem() != null) {
801                Train train = (Train) trainBox.getSelectedItem();
802                if (train.isBuilt() &&
803                        train.getTerminationTrack() != null &&
804                        train.getTerminationTrack().getLocation() == destination) {
805                    trackDestinationBox.addItem(train.getTerminationTrack());
806                    trackDestinationBox.setSelectedItem(track);
807                }
808            }
809            if (_rs != null &&
810                    _rs.getDestination() != null &&
811                    _rs.getDestination().equals(destination) &&
812                    _rs.getDestinationTrack() != null) {
813                trackDestinationBox.setSelectedItem(_rs.getDestinationTrack());
814            } else if (track != null) {
815                trackDestinationBox.setSelectedItem(track);
816            }
817        }
818    }
819
820    protected void updateTrainComboBox() {
821        log.debug("update train combo box");
822        trainManager.updateTrainComboBox(trainBox);
823        if (_rs != null) {
824            trainBox.setSelectedItem(_rs.getTrain());
825        }
826    }
827
828    @Override
829    public void dispose() {
830        if (_rs != null) {
831            _rs.removePropertyChangeListener(this);
832        }
833        locationManager.removePropertyChangeListener(this);
834        trainManager.removePropertyChangeListener(this);
835        super.dispose();
836    }
837
838    @Override
839    public void propertyChange(java.beans.PropertyChangeEvent e) {
840        log.debug("PropertyChange ({}) new: ({})", e.getPropertyName(), e.getNewValue());
841        if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) {
842            updateComboBoxes();
843        }
844        if (e.getPropertyName().equals(TrainManager.LISTLENGTH_CHANGED_PROPERTY)) {
845            updateTrainComboBox();
846        }
847        if (e.getPropertyName().equals(RollingStock.TRACK_CHANGED_PROPERTY)) {
848            updateLocationComboBoxes();
849        }
850        if (e.getPropertyName().equals(RollingStock.DESTINATION_TRACK_CHANGED_PROPERTY)) {
851            updateDestinationComboBoxes();
852        }
853        if (e.getPropertyName().equals(RollingStock.TRAIN_CHANGED_PROPERTY)) {
854            if (_rs != null) {
855                trainBox.setSelectedItem(_rs.getTrain());
856            }
857        }
858    }
859
860    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RollingStockSetFrame.class);
861}