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