001package jmri.jmrit.operations.trains;
002
003import java.awt.*;
004import java.text.MessageFormat;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.swing.*;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import jmri.InstanceManager;
014import jmri.jmrit.operations.*;
015import jmri.jmrit.operations.locations.Location;
016import jmri.jmrit.operations.locations.LocationManager;
017import jmri.jmrit.operations.rollingstock.RollingStock;
018import jmri.jmrit.operations.rollingstock.cars.*;
019import jmri.jmrit.operations.rollingstock.engines.*;
020import jmri.jmrit.operations.routes.*;
021import jmri.jmrit.operations.setup.Control;
022import jmri.jmrit.operations.setup.Setup;
023import jmri.jmrit.operations.trains.tools.*;
024
025/**
026 * Frame for user edit of a train
027 *
028 * @author Dan Boudreau Copyright (C) 2008, 2011, 2012, 2013, 2014
029 */
030public class TrainEditFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
031
032    TrainManager trainManager = InstanceManager.getDefault(TrainManager.class);
033    RouteManager routeManager = InstanceManager.getDefault(RouteManager.class);
034
035    public Train _train = null;
036    List<JCheckBox> typeCarCheckBoxes = new ArrayList<>();
037    List<JCheckBox> typeEngineCheckBoxes = new ArrayList<>();
038    List<JCheckBox> locationCheckBoxes = new ArrayList<>();
039    JPanel typeCarPanelCheckBoxes = new JPanel();
040    JPanel typeEnginePanelCheckBoxes = new JPanel();
041    JPanel roadAndLoadStatusPanel = new JPanel();
042    JPanel locationPanelCheckBoxes = new JPanel();
043    JScrollPane typeCarPane;
044    JScrollPane typeEnginePane;
045    JScrollPane locationsPane;
046
047    // labels
048    JLabel textRouteStatus = new JLabel();
049    JLabel textModel = new JLabel(Bundle.getMessage("Model"));
050    JLabel textRoad2 = new JLabel(Bundle.getMessage("Road"));
051    JLabel textRoad3 = new JLabel(Bundle.getMessage("Road"));
052    JLabel textEngine = new JLabel(Bundle.getMessage("Engines"));
053
054    // major buttons
055    JButton editButton = new JButton(Bundle.getMessage("ButtonEdit")); // edit route
056    JButton clearButton = new JButton(Bundle.getMessage("ClearAll"));
057    JButton setButton = new JButton(Bundle.getMessage("SelectAll"));
058    JButton resetButton = new JButton(Bundle.getMessage("ResetTrain"));
059    JButton saveTrainButton = new JButton(Bundle.getMessage("SaveTrain"));
060    JButton deleteTrainButton = new JButton(Bundle.getMessage("DeleteTrain"));
061    JButton addTrainButton = new JButton(Bundle.getMessage("AddTrain"));
062
063    // alternate buttons
064    JButton loadOptionButton = new JButton(Bundle.getMessage("AcceptAll"));
065    JButton roadOptionButton = new JButton(Bundle.getMessage("AcceptAll"));
066
067    // radio buttons
068    JRadioButton noneRadioButton = new JRadioButton(Bundle.getMessage("None"));
069    JRadioButton cabooseRadioButton = new JRadioButton(Bundle.getMessage("Caboose"));
070    JRadioButton fredRadioButton = new JRadioButton(Bundle.getMessage("FRED"));
071    ButtonGroup group = new ButtonGroup();
072
073    // text field
074    JTextField trainNameTextField = new JTextField(Control.max_len_string_train_name - 5); // make slightly smaller
075    JTextField trainDescriptionTextField = new JTextField(30);
076
077    // text area
078    JTextArea commentTextArea = new JTextArea(2, 70);
079    JScrollPane commentScroller = new JScrollPane(commentTextArea);
080    JColorChooser commentColorChooser = new JColorChooser(Color.black);
081
082    // for padding out panel
083    JLabel space1 = new JLabel(" "); // before hour
084    JLabel space2 = new JLabel(" "); // between hour and minute
085    JLabel space3 = new JLabel(" "); // after minute
086    JLabel space4 = new JLabel(" "); // between route and edit
087    JLabel space5 = new JLabel(" "); // after edit
088
089    // combo boxes
090    JComboBox<String> hourBox = new JComboBox<>();
091    JComboBox<String> minuteBox = new JComboBox<>();
092    JComboBox<Route> routeBox = routeManager.getComboBox();
093    JComboBox<String> roadCabooseBox = new JComboBox<>();
094    JComboBox<String> roadEngineBox = new JComboBox<>();
095    JComboBox<String> modelEngineBox = InstanceManager.getDefault(EngineModels.class).getComboBox();
096    JComboBox<String> numEnginesBox = new JComboBox<>();
097
098    JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
099
100    public static final String DISPOSE = "dispose"; // NOI18N
101
102    public TrainEditFrame(Train train) {
103        super(Bundle.getMessage("TitleTrainEdit"));
104        // Set up the jtable in a Scroll Pane..
105        locationsPane = new JScrollPane(locationPanelCheckBoxes);
106        locationsPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
107        locationsPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Stops")));
108
109        typeCarPane = new JScrollPane(typeCarPanelCheckBoxes);
110        typeCarPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TypesCar")));
111        typeCarPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
112
113        typeEnginePane = new JScrollPane(typeEnginePanelCheckBoxes);
114        typeEnginePane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
115        typeEnginePane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TypesEngine")));
116
117        _train = train;
118
119        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
120
121        // Set up the panels
122        JPanel p = new JPanel();
123        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
124        JScrollPane pPane = new JScrollPane(p);
125        pPane.setMinimumSize(new Dimension(300, 5 * trainNameTextField.getPreferredSize().height));
126        pPane.setBorder(BorderFactory.createTitledBorder(""));
127
128        // Layout the panel by rows
129        // row 1
130        JPanel p1 = new JPanel();
131        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
132        // row 1a
133        JPanel pName = new JPanel();
134        pName.setLayout(new GridBagLayout());
135        pName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Name")));
136        addItem(pName, trainNameTextField, 0, 0);
137        // row 1b
138        JPanel pDesc = new JPanel();
139        pDesc.setLayout(new GridBagLayout());
140        pDesc.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Description")));
141        trainDescriptionTextField.setToolTipText(Bundle.getMessage("TipTrainDescription"));
142        addItem(pDesc, trainDescriptionTextField, 0, 0);
143
144        p1.add(pName);
145        p1.add(pDesc);
146
147        // row 2
148        JPanel p2 = new JPanel();
149        p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS));
150        // row 2a
151        JPanel pdt = new JPanel();
152        pdt.setLayout(new GridBagLayout());
153        pdt.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("DepartTime")));
154
155        // build hour and minute menus
156        hourBox.setPrototypeDisplayValue("000"); // needed for font size 9
157        minuteBox.setPrototypeDisplayValue("000");
158        for (int i = 0; i < 24; i++) {
159            if (i < 10) {
160                hourBox.addItem("0" + Integer.toString(i));
161            } else {
162                hourBox.addItem(Integer.toString(i));
163            }
164        }
165        for (int i = 0; i < 60; i += 1) {
166            if (i < 10) {
167                minuteBox.addItem("0" + Integer.toString(i));
168            } else {
169                minuteBox.addItem(Integer.toString(i));
170            }
171        }
172
173        addItem(pdt, space1, 0, 5);
174        addItem(pdt, hourBox, 1, 5);
175        addItem(pdt, space2, 2, 5);
176        addItem(pdt, minuteBox, 3, 5);
177        addItem(pdt, space3, 4, 5);
178        // row 2b
179        // BUG! routeBox needs its own panel when resizing frame!
180        JPanel pr = new JPanel();
181        pr.setLayout(new GridBagLayout());
182        pr.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Route")));
183        addItem(pr, routeBox, 0, 5);
184        addItem(pr, space4, 1, 5);
185        addItem(pr, editButton, 2, 5);
186        addItem(pr, space5, 3, 5);
187        addItem(pr, textRouteStatus, 4, 5);
188
189        p2.add(pdt);
190        p2.add(pr);
191
192        p.add(p1);
193        p.add(p2);
194
195        // row 5
196        locationPanelCheckBoxes.setLayout(new GridBagLayout());
197
198        // row 6
199        typeCarPanelCheckBoxes.setLayout(new GridBagLayout());
200
201        // row 8
202        typeEnginePanelCheckBoxes.setLayout(new GridBagLayout());
203
204        // status panel for roads and loads
205        roadAndLoadStatusPanel.setLayout(new BoxLayout(roadAndLoadStatusPanel, BoxLayout.X_AXIS));
206        JPanel pRoadOption = new JPanel();
207        pRoadOption.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RoadOption")));
208        pRoadOption.add(roadOptionButton);
209        roadOptionButton.addActionListener(new TrainRoadOptionsAction(this));
210
211        JPanel pLoadOption = new JPanel();
212        pLoadOption.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LoadOption")));
213        pLoadOption.add(loadOptionButton);
214        loadOptionButton.addActionListener(new TrainLoadOptionsAction(this));
215
216        roadAndLoadStatusPanel.add(pRoadOption);
217        roadAndLoadStatusPanel.add(pLoadOption);
218        roadAndLoadStatusPanel.setVisible(false); // don't show unless there's a restriction
219
220        // row 10
221        JPanel trainReq = new JPanel();
222        trainReq.setLayout(new GridBagLayout());
223        trainReq.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainRequires")));
224
225        for (int i = 0; i < Setup.getMaxNumberEngines() + 1; i++) {
226            numEnginesBox.addItem(Integer.toString(i));
227        }
228        numEnginesBox.addItem(Train.AUTO);
229        numEnginesBox.setMinimumSize(new Dimension(100, 20));
230        numEnginesBox.setToolTipText(Bundle.getMessage("TipNumberOfLocos"));
231        addItem(trainReq, textEngine, 1, 1);
232        addItem(trainReq, numEnginesBox, 2, 1);
233        addItem(trainReq, textModel, 3, 1);
234        modelEngineBox.insertItemAt(NONE, 0);
235        modelEngineBox.setSelectedIndex(0);
236        modelEngineBox.setMinimumSize(new Dimension(120, 20));
237        modelEngineBox.setToolTipText(Bundle.getMessage("ModelEngineTip"));
238        addItem(trainReq, modelEngineBox, 4, 1);
239        addItem(trainReq, textRoad2, 5, 1);
240        roadEngineBox.insertItemAt(NONE, 0);
241        roadEngineBox.setSelectedIndex(0);
242        roadEngineBox.setMinimumSize(new Dimension(120, 20));
243        roadEngineBox.setToolTipText(Bundle.getMessage("RoadEngineTip"));
244        addItem(trainReq, roadEngineBox, 6, 1);
245
246        JPanel trainLastCar = new JPanel();
247        trainLastCar.setLayout(new GridBagLayout());
248        trainLastCar.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainLastCar")));
249
250        addItem(trainLastCar, noneRadioButton, 2, 2);
251        noneRadioButton.setToolTipText(Bundle.getMessage("TipNoCabooseOrFRED"));
252        addItem(trainLastCar, fredRadioButton, 3, 2);
253        fredRadioButton.setToolTipText(Bundle.getMessage("TipFRED"));
254        addItem(trainLastCar, cabooseRadioButton, 4, 2);
255        cabooseRadioButton.setToolTipText(Bundle.getMessage("TipCaboose"));
256        addItem(trainLastCar, textRoad3, 5, 2);
257        roadCabooseBox.setMinimumSize(new Dimension(120, 20));
258        roadCabooseBox.setToolTipText(Bundle.getMessage("RoadCabooseTip"));
259        addItem(trainLastCar, roadCabooseBox, 6, 2);
260        group.add(noneRadioButton);
261        group.add(cabooseRadioButton);
262        group.add(fredRadioButton);
263        noneRadioButton.setSelected(true);
264
265        // row 13 comment
266        JPanel pC = new JPanel();
267        pC.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment")));
268        pC.setLayout(new GridBagLayout());
269        addItem(pC, commentScroller, 1, 0);
270        if (_train != null) {
271            addItem(pC, OperationsPanel.getColorChooserPanel(_train.getCommentWithColor(), commentColorChooser), 2, 0);
272        } else {
273            addItem(pC, OperationsPanel.getColorChooserPanel("", commentColorChooser), 2, 0);
274        }
275
276        // adjust text area width based on window size less color chooser
277        Dimension d = new Dimension(getPreferredSize().width - 100, getPreferredSize().height);
278        adjustTextAreaColumnWidth(commentScroller, commentTextArea, d);
279
280        // row 15 buttons
281        JPanel pB = new JPanel();
282        pB.setLayout(new GridBagLayout());
283        addItem(pB, deleteTrainButton, 0, 0);
284        addItem(pB, resetButton, 1, 0);
285        addItem(pB, addTrainButton, 2, 0);
286        addItem(pB, saveTrainButton, 3, 0);
287
288        getContentPane().add(pPane);
289        getContentPane().add(locationsPane);
290        getContentPane().add(typeCarPane);
291        getContentPane().add(typeEnginePane);
292        getContentPane().add(roadAndLoadStatusPanel);
293        getContentPane().add(trainReq);
294        getContentPane().add(trainLastCar);
295        getContentPane().add(pC);
296        getContentPane().add(pB);
297
298        // setup buttons
299        addButtonAction(editButton);
300        addButtonAction(setButton);
301        addButtonAction(clearButton);
302        addButtonAction(resetButton);
303        addButtonAction(deleteTrainButton);
304        addButtonAction(addTrainButton);
305        addButtonAction(saveTrainButton);
306
307        addRadioButtonAction(noneRadioButton);
308        addRadioButtonAction(cabooseRadioButton);
309        addRadioButtonAction(fredRadioButton);
310
311        // tool tips
312        resetButton.setToolTipText(Bundle.getMessage("TipTrainReset"));
313
314        // build menu
315        JMenuBar menuBar = new JMenuBar();
316        menuBar.add(toolMenu);
317        loadToolMenu(toolMenu);
318        setJMenuBar(menuBar);
319        addHelpMenu("package.jmri.jmrit.operations.Operations_TrainEdit", true); // NOI18N
320
321        if (_train != null) {
322            trainNameTextField.setText(_train.getName());
323            trainDescriptionTextField.setText(_train.getRawDescription());
324            routeBox.setSelectedItem(_train.getRoute());
325            modelEngineBox.setSelectedItem(_train.getEngineModel());
326            commentTextArea.setText(TrainCommon.getTextColorString(_train.getCommentWithColor()));
327            cabooseRadioButton.setSelected(_train.isCabooseNeeded());
328            fredRadioButton.setSelected(_train.isFredNeeded());
329            updateDepartureTime();
330            enableButtons(true);
331            // listen for train changes
332            _train.addPropertyChangeListener(this);
333
334            Route route = _train.getRoute();
335            if (route != null) {
336                if (_train.getTrainDepartsRouteLocation() != null &&
337                        _train.getTrainDepartsRouteLocation().getLocation() != null &&
338                        !_train.getTrainDepartsRouteLocation().getLocation().isStaging())
339                    numEnginesBox.addItem(Train.AUTO_HPT);
340            }
341            numEnginesBox.setSelectedItem(_train.getNumberEngines());
342        } else {
343            setTitle(Bundle.getMessage("TitleTrainAdd"));
344            enableButtons(false);
345        }
346
347        modelEngineBox.setEnabled(!numEnginesBox.getSelectedItem().equals("0"));
348        roadEngineBox.setEnabled(!numEnginesBox.getSelectedItem().equals("0"));
349
350        // load route location checkboxes
351        updateLocationCheckboxes();
352        updateCarTypeCheckboxes();
353        updateEngineTypeCheckboxes();
354        updateRoadAndLoadStatus();
355        updateCabooseRoadComboBox();
356        updateEngineRoadComboBox();
357
358        // setup combobox
359        addComboBoxAction(numEnginesBox);
360        addComboBoxAction(routeBox);
361        addComboBoxAction(modelEngineBox);
362
363        // get notified if combo box gets modified
364        routeManager.addPropertyChangeListener(this);
365        // get notified if car types or roads gets modified
366        InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this);
367        InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this);
368        InstanceManager.getDefault(EngineTypes.class).addPropertyChangeListener(this);
369        InstanceManager.getDefault(EngineModels.class).addPropertyChangeListener(this);
370        InstanceManager.getDefault(LocationManager.class).addPropertyChangeListener(this);
371
372        initMinimumSize(new Dimension(Control.panelWidth600, Control.panelHeight600));
373    }
374
375    private void loadToolMenu(JMenu toolMenu) {
376        toolMenu.removeAll();
377        // first 5 menu items will also close when the edit train window closes
378        toolMenu.add(new TrainEditBuildOptionsAction(this));
379        toolMenu.add(new TrainLoadOptionsAction(this));
380        toolMenu.add(new TrainRoadOptionsAction(this));
381        toolMenu.add(new TrainManifestOptionAction(this));
382        toolMenu.add(new TrainScriptAction(this));
383        toolMenu.add(new TrainCopyAction(_train));
384        toolMenu.add(new TrainConductorAction(_train));
385        toolMenu.addSeparator();
386        toolMenu.add(new TrainByCarTypeAction(_train));
387        toolMenu.addSeparator();
388        toolMenu.add(new PrintTrainAction(false, this));
389        toolMenu.add(new PrintTrainAction(true, this));
390        toolMenu.add(new PrintTrainManifestAction(false, _train));
391        toolMenu.add(new PrintTrainManifestAction(true, _train));
392        toolMenu.add(new PrintTrainBuildReportAction(false, _train));
393        toolMenu.add(new PrintTrainBuildReportAction(true, _train));
394        toolMenu.add(new PrintSavedTrainManifestAction(false, _train));
395        toolMenu.add(new PrintSavedTrainManifestAction(true, _train));
396        toolMenu.add(new PrintSavedBuildReportAction(false, _train));
397        toolMenu.add(new PrintSavedBuildReportAction(true, _train));
398    }
399
400    // Save, Delete, Add, Edit, Reset, Set, Clear
401    @Override
402    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
403        Train train = trainManager.getTrainByName(trainNameTextField.getText().trim());
404        if (ae.getSource() == saveTrainButton) {
405            log.debug("train save button activated");
406            if (_train == null && train == null) {
407                saveNewTrain(); // this can't happen, Save button is disabled
408            } else {
409                if (train != null && train != _train) {
410                    reportTrainExists(Bundle.getMessage("save"));
411                    return;
412                }
413                checkRoute(); // check to see if use supplied a route, just warn if no route
414                saveTrain();
415            }
416            if (Setup.isCloseWindowOnSaveEnabled()) {
417                dispose();
418            }
419        }
420        if (ae.getSource() == deleteTrainButton) {
421            log.debug("train delete button activated");
422            if (train == null) {
423                return;
424            }
425            if (!_train.reset()) {
426                JOptionPane.showMessageDialog(this,
427                        MessageFormat.format(Bundle.getMessage("TrainIsInRoute"),
428                                new Object[] { train.getTrainTerminatesName() }),
429                        Bundle.getMessage("CanNotDeleteTrain"), JOptionPane.ERROR_MESSAGE);
430                return;
431            }
432            if (JOptionPane.showConfirmDialog(this,
433                    MessageFormat.format(Bundle.getMessage("deleteMsg"), new Object[] { train.getName() }),
434                    Bundle.getMessage("deleteTrain"), JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
435                return;
436            }
437            routeBox.setSelectedItem(null);
438            trainManager.deregister(train);
439            for (Frame frame : children) {
440                frame.dispose();
441            }
442            _train = null;
443            enableButtons(false);
444            // save train file
445            OperationsXml.save();
446        }
447        if (ae.getSource() == addTrainButton) {
448            if (train != null) {
449                reportTrainExists(Bundle.getMessage("add"));
450                return;
451            }
452            saveNewTrain();
453        }
454        if (ae.getSource() == editButton) {
455            editAddRoute();
456        }
457        if (ae.getSource() == setButton) {
458            selectCheckboxes(true);
459        }
460        if (ae.getSource() == clearButton) {
461            selectCheckboxes(false);
462        }
463        if (ae.getSource() == resetButton) {
464            if (_train != null) {
465                if (!_train.reset()) {
466                    JOptionPane.showMessageDialog(this,
467                            MessageFormat.format(Bundle.getMessage("TrainIsInRoute"),
468                                    new Object[] { _train.getTrainTerminatesName() }),
469                            Bundle.getMessage("CanNotResetTrain"), JOptionPane.ERROR_MESSAGE);
470                }
471            }
472        }
473    }
474
475    @Override
476    public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) {
477        log.debug("radio button activated");
478        if (_train != null) {
479            if (ae.getSource() == noneRadioButton ||
480                    ae.getSource() == cabooseRadioButton ||
481                    ae.getSource() == fredRadioButton) {
482                updateCabooseRoadComboBox();
483            }
484        }
485    }
486
487    private void saveNewTrain() {
488        if (!checkName(Bundle.getMessage("add"))) {
489            return;
490        }
491        Train train = trainManager.newTrain(trainNameTextField.getText());
492        _train = train;
493        _train.addPropertyChangeListener(this);
494
495        // update check boxes
496        updateCarTypeCheckboxes();
497        updateEngineTypeCheckboxes();
498        // enable check boxes and buttons
499        enableButtons(true);
500        saveTrain();
501        loadToolMenu(toolMenu);
502    }
503
504    private void saveTrain() {
505        if (!checkName(Bundle.getMessage("save"))) {
506            return;
507        }
508        if (!checkModel() || !checkEngineRoad()) {
509            return;
510        }
511        if (!_train.getName().equals(trainNameTextField.getText().trim()) ||
512                !_train.getRawDescription().equals(trainDescriptionTextField.getText()) ||
513                !_train.getCommentWithColor().equals(
514                        TrainCommon.formatColorString(commentTextArea.getText(), commentColorChooser.getColor()))) {
515            _train.setModified(true);
516        }
517        _train.setDepartureTime(hourBox.getSelectedItem().toString(), minuteBox.getSelectedItem().toString());
518        _train.setNumberEngines((String) numEnginesBox.getSelectedItem());
519        if (_train.getNumberEngines().equals("0")) {
520            modelEngineBox.setSelectedIndex(0);
521            roadEngineBox.setSelectedIndex(0);
522        }
523        _train.setEngineRoad((String) roadEngineBox.getSelectedItem());
524        _train.setEngineModel((String) modelEngineBox.getSelectedItem());
525        if (cabooseRadioButton.isSelected()) {
526            _train.setRequirements(Train.CABOOSE);
527        }
528        if (fredRadioButton.isSelected()) {
529            _train.setRequirements(Train.FRED);
530        }
531        if (noneRadioButton.isSelected()) {
532            _train.setRequirements(Train.NO_CABOOSE_OR_FRED);
533        }
534        _train.setCabooseRoad((String) roadCabooseBox.getSelectedItem());
535        _train.setName(trainNameTextField.getText().trim());
536        _train.setDescription(trainDescriptionTextField.getText());
537        _train.setComment(TrainCommon.formatColorString(commentTextArea.getText(), commentColorChooser.getColor()));
538        // save train file
539        OperationsXml.save();
540    }
541
542    /**
543     *
544     * @return true if name isn't too long and is at least one character
545     */
546    private boolean checkName(String s) {
547        String trainName = trainNameTextField.getText().trim();
548        if (trainName.isEmpty()) {
549            log.debug("Must enter a train name");
550            JOptionPane.showMessageDialog(this, Bundle.getMessage("MustEnterName"),
551                    MessageFormat.format(Bundle.getMessage("CanNot"), new Object[] { s }), JOptionPane.ERROR_MESSAGE);
552            return false;
553        }
554        if (trainName.length() > Control.max_len_string_train_name) {
555            JOptionPane.showMessageDialog(this,
556                    MessageFormat.format(Bundle.getMessage("TrainNameLess"),
557                            new Object[] { Control.max_len_string_train_name + 1 }),
558                    MessageFormat.format(Bundle.getMessage("CanNot"), new Object[] { s }), JOptionPane.ERROR_MESSAGE);
559            return false;
560        }
561        if (!OperationsXml.checkFileName(trainName)) { // NOI18N
562            log.error("Train name must not contain reserved characters");
563            JOptionPane.showMessageDialog(this,
564                    Bundle.getMessage("NameResChar") + NEW_LINE + Bundle.getMessage("ReservedChar"),
565                    MessageFormat.format(Bundle.getMessage("CanNot"), new Object[] { s }), JOptionPane.ERROR_MESSAGE);
566            return false;
567        }
568
569        return true;
570    }
571
572    private boolean checkModel() {
573        String model = (String) modelEngineBox.getSelectedItem();
574        if (numEnginesBox.getSelectedItem().equals("0") || model.equals(NONE)) {
575            return true;
576        }
577        String type = InstanceManager.getDefault(EngineModels.class).getModelType(model);
578        if (!_train.isTypeNameAccepted(type)) {
579            JOptionPane.showMessageDialog(this,
580                    MessageFormat.format(Bundle.getMessage("TrainModelService"), new Object[] { model, type }),
581                    MessageFormat.format(Bundle.getMessage("CanNot"), new Object[] { Bundle.getMessage("save") }),
582                    JOptionPane.ERROR_MESSAGE);
583            return false;
584        }
585        if (roadEngineBox.getItemCount() == 1) {
586            log.debug("No locos available that match the model selected!");
587            JOptionPane.showMessageDialog(this,
588                    MessageFormat.format(Bundle.getMessage("NoLocosModel"), new Object[] { model }),
589                    MessageFormat.format(Bundle.getMessage("TrainWillNotBuild"), new Object[] { _train.getName() }),
590                    JOptionPane.WARNING_MESSAGE);
591        }
592        return true;
593    }
594
595    private boolean checkEngineRoad() {
596        String road = (String) roadEngineBox.getSelectedItem();
597        if (numEnginesBox.getSelectedItem().equals("0") || road.equals(NONE)) {
598            return true;
599        }
600        if (!road.equals(NONE) && !_train.isLocoRoadNameAccepted(road)) {
601            JOptionPane.showMessageDialog(this,
602                    MessageFormat.format(Bundle.getMessage("TrainNotThisRoad"), new Object[] { _train.getName(), road }),
603                    MessageFormat.format(Bundle.getMessage("TrainWillNotBuild"), new Object[] { _train.getName() }),
604                    JOptionPane.WARNING_MESSAGE);
605            return false;
606        }
607        for (RollingStock rs : InstanceManager.getDefault(EngineManager.class).getList()) {
608            if (!_train.isTypeNameAccepted(rs.getTypeName())) {
609                continue;
610            }
611            if (rs.getRoadName().equals(road)) {
612                return true;
613            }
614        }
615        JOptionPane.showMessageDialog(this,
616                MessageFormat.format(Bundle.getMessage("NoLocoRoad"), new Object[] { road }),
617                MessageFormat.format(Bundle.getMessage("TrainWillNotBuild"), new Object[] { _train.getName() }),
618                JOptionPane.WARNING_MESSAGE);
619        return false; // couldn't find a loco with the selected road
620    }
621
622    private boolean checkRoute() {
623        if (_train.getRoute() == null) {
624            JOptionPane.showMessageDialog(this, Bundle.getMessage("TrainNeedsRoute"), Bundle.getMessage("TrainNoRoute"),
625                    JOptionPane.WARNING_MESSAGE);
626            return false;
627        }
628        return true;
629
630    }
631
632    private void reportTrainExists(String s) {
633        log.debug("Can not {}, train already exists", s);
634        JOptionPane.showMessageDialog(this, Bundle.getMessage("TrainNameExists"),
635                MessageFormat.format(Bundle.getMessage("CanNot"), new Object[] { s }), JOptionPane.ERROR_MESSAGE);
636    }
637
638    private void enableButtons(boolean enabled) {
639        toolMenu.setEnabled(enabled);
640        editButton.setEnabled(enabled);
641        routeBox.setEnabled(enabled && _train != null && !_train.isBuilt());
642        clearButton.setEnabled(enabled);
643        resetButton.setEnabled(enabled);
644        setButton.setEnabled(enabled);
645        saveTrainButton.setEnabled(enabled);
646        deleteTrainButton.setEnabled(enabled);
647        numEnginesBox.setEnabled(enabled);
648        enableCheckboxes(enabled);
649        noneRadioButton.setEnabled(enabled);
650        fredRadioButton.setEnabled(enabled);
651        cabooseRadioButton.setEnabled(enabled);
652        roadOptionButton.setEnabled(enabled);
653        loadOptionButton.setEnabled(enabled);
654        // the inverse!
655        addTrainButton.setEnabled(!enabled);
656    }
657
658    private void selectCheckboxes(boolean enable) {
659        for (int i = 0; i < typeCarCheckBoxes.size(); i++) {
660            JCheckBox checkBox = typeCarCheckBoxes.get(i);
661            checkBox.setSelected(enable);
662            if (_train != null) {
663                _train.removePropertyChangeListener(this);
664                if (enable) {
665                    _train.addTypeName(checkBox.getText());
666                } else {
667                    _train.deleteTypeName(checkBox.getText());
668                }
669                _train.addPropertyChangeListener(this);
670            }
671        }
672    }
673
674    @Override
675    public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) {
676        if (_train == null) {
677            return;
678        }
679        if (ae.getSource() == numEnginesBox) {
680            modelEngineBox.setEnabled(!numEnginesBox.getSelectedItem().equals("0"));
681            roadEngineBox.setEnabled(!numEnginesBox.getSelectedItem().equals("0"));
682        }
683        if (ae.getSource() == modelEngineBox) {
684            updateEngineRoadComboBox();
685        }
686        if (ae.getSource() == routeBox) {
687            if (routeBox.isEnabled()) {
688                Route route = _train.getRoute();
689                if (route != null) {
690                    route.removePropertyChangeListener(this);
691                }
692                Object selected = routeBox.getSelectedItem();
693                if (selected != null) {
694                    route = (Route) selected;
695                    _train.setRoute(route);
696                    route.addPropertyChangeListener(this);
697                } else {
698                    _train.setRoute(null);
699                }
700                updateLocationCheckboxes();
701                pack();
702                repaint();
703            }
704        }
705    }
706
707    private void enableCheckboxes(boolean enable) {
708        for (int i = 0; i < typeCarCheckBoxes.size(); i++) {
709            typeCarCheckBoxes.get(i).setEnabled(enable);
710        }
711        for (int i = 0; i < typeEngineCheckBoxes.size(); i++) {
712            typeEngineCheckBoxes.get(i).setEnabled(enable);
713        }
714    }
715
716    private void addLocationCheckBoxAction(JCheckBox b) {
717        b.addActionListener(new java.awt.event.ActionListener() {
718            @Override
719            public void actionPerformed(java.awt.event.ActionEvent e) {
720                locationCheckBoxActionPerformed(e);
721            }
722        });
723    }
724
725    public void locationCheckBoxActionPerformed(java.awt.event.ActionEvent ae) {
726        JCheckBox b = (JCheckBox) ae.getSource();
727        log.debug("checkbox change {}", b.getText());
728        if (_train == null) {
729            return;
730        }
731        String id = b.getName();
732        if (b.isSelected()) {
733            _train.deleteTrainSkipsLocation(id);
734        } else {
735            // check to see if skipped location is staging
736            if (_train.getRoute().getLocationById(id).getLocation().isStaging()) {
737                int result = JOptionPane.showConfirmDialog(this,
738                        MessageFormat.format(Bundle.getMessage("TrainRouteStaging"),
739                                new Object[] { _train.getName(), _train.getRoute().getLocationById(id).getName() }),
740                        Bundle.getMessage("TrainRouteNotStaging"), JOptionPane.OK_CANCEL_OPTION);
741                if (result == JOptionPane.CANCEL_OPTION) {
742                    b.setSelected(true);
743                    return; // don't skip staging
744                }
745            }
746            _train.addTrainSkipsLocation(id);
747        }
748    }
749
750    private void updateRouteComboBox() {
751        routeBox.setEnabled(false);
752        routeManager.updateComboBox(routeBox);
753        if (_train != null) {
754            routeBox.setSelectedItem(_train.getRoute());
755        }
756        routeBox.setEnabled(true);
757    }
758
759    private void updateCarTypeCheckboxes() {
760        typeCarCheckBoxes.clear();
761        typeCarPanelCheckBoxes.removeAll();
762        loadCarTypes();
763        enableCheckboxes(_train != null);
764        typeCarPanelCheckBoxes.revalidate();
765        repaint();
766    }
767
768    private void loadCarTypes() {
769        int numberOfCheckboxes = getNumberOfCheckboxesPerLine(); // number per line
770        int x = 0;
771        int y = 1; // vertical position in panel
772        for (String type : InstanceManager.getDefault(CarTypes.class).getNames()) {
773            JCheckBox checkBox = new javax.swing.JCheckBox();
774            typeCarCheckBoxes.add(checkBox);
775            checkBox.setText(type);
776            addTypeCheckBoxAction(checkBox);
777            addItemLeft(typeCarPanelCheckBoxes, checkBox, x++, y);
778            if (_train != null && _train.isTypeNameAccepted(type)) {
779                checkBox.setSelected(true);
780            }
781            if (x > numberOfCheckboxes) {
782                y++;
783                x = 0;
784            }
785        }
786
787        JPanel p = new JPanel();
788        p.add(clearButton);
789        p.add(setButton);
790        GridBagConstraints gc = new GridBagConstraints();
791        gc.gridwidth = getNumberOfCheckboxesPerLine() + 1;
792        gc.gridy = ++y;
793        typeCarPanelCheckBoxes.add(p, gc);
794
795    }
796
797    private void updateEngineTypeCheckboxes() {
798        typeEngineCheckBoxes.clear();
799        typeEnginePanelCheckBoxes.removeAll();
800        loadEngineTypes();
801        enableCheckboxes(_train != null);
802        typeEnginePanelCheckBoxes.revalidate();
803        repaint();
804    }
805
806    private void loadEngineTypes() {
807        int numberOfCheckboxes = getNumberOfCheckboxesPerLine(); // number per line
808        int x = 0;
809        int y = 1;
810        for (String type : InstanceManager.getDefault(EngineTypes.class).getNames()) {
811            JCheckBox checkBox = new javax.swing.JCheckBox();
812            typeEngineCheckBoxes.add(checkBox);
813            checkBox.setText(type);
814            addTypeCheckBoxAction(checkBox);
815            addItemLeft(typeEnginePanelCheckBoxes, checkBox, x++, y);
816            if (_train != null && _train.isTypeNameAccepted(type)) {
817                checkBox.setSelected(true);
818            }
819            if (x > numberOfCheckboxes) {
820                y++;
821                x = 0;
822            }
823        }
824    }
825
826    private void updateRoadComboBoxes() {
827        updateCabooseRoadComboBox();
828        updateEngineRoadComboBox();
829    }
830
831    // update caboose road box based on radio selection
832    private void updateCabooseRoadComboBox() {
833        roadCabooseBox.removeAllItems();
834        roadCabooseBox.addItem(NONE);
835        if (noneRadioButton.isSelected()) {
836            roadCabooseBox.setEnabled(false);
837            return;
838        }
839        roadCabooseBox.setEnabled(true);
840        List<String> roads;
841        if (cabooseRadioButton.isSelected()) {
842            roads = InstanceManager.getDefault(CarManager.class).getCabooseRoadNames();
843        } else {
844            roads = InstanceManager.getDefault(CarManager.class).getFredRoadNames();
845        }
846        for (String road : roads) {
847            roadCabooseBox.addItem(road);
848        }
849        if (_train != null) {
850            roadCabooseBox.setSelectedItem(_train.getCabooseRoad());
851        }
852    }
853
854    private void updateEngineRoadComboBox() {
855        String engineModel = (String) modelEngineBox.getSelectedItem();
856        if (engineModel == null) {
857            return;
858        }
859        roadEngineBox.removeAllItems();
860        roadEngineBox.addItem(NONE);
861        List<String> roads = InstanceManager.getDefault(EngineManager.class).getEngineRoadNames(engineModel);
862        for (String roadName : roads) {
863            roadEngineBox.addItem(roadName);
864        }
865        if (_train != null) {
866            roadEngineBox.setSelectedItem(_train.getEngineRoad());
867        }
868    }
869
870    private void addTypeCheckBoxAction(JCheckBox b) {
871        b.addActionListener(new java.awt.event.ActionListener() {
872            @Override
873            public void actionPerformed(java.awt.event.ActionEvent e) {
874                typeCheckBoxActionPerformed(e);
875            }
876        });
877    }
878
879    public void typeCheckBoxActionPerformed(java.awt.event.ActionEvent ae) {
880        JCheckBox b = (JCheckBox) ae.getSource();
881        log.debug("checkbox change {}", b.getText());
882        if (_train == null) {
883            return;
884        }
885        if (b.isSelected()) {
886            _train.addTypeName(b.getText());
887        } else {
888            _train.deleteTypeName(b.getText());
889        }
890    }
891
892    // the train's route shown as locations with checkboxes
893    private void updateLocationCheckboxes() {
894        updateRouteStatus();
895        locationCheckBoxes.clear();
896        locationPanelCheckBoxes.removeAll();
897        int y = 0; // vertical position in panel
898        Route route = null;
899        if (_train != null) {
900            route = _train.getRoute();
901        }
902        if (route != null) {
903            List<RouteLocation> routeList = route.getLocationsBySequenceList();
904            for (RouteLocation rl : routeList) {
905                JCheckBox checkBox = new javax.swing.JCheckBox();
906                locationCheckBoxes.add(checkBox);
907                checkBox.setText(rl.toString());
908                checkBox.setName(rl.getId());
909                addItemLeft(locationPanelCheckBoxes, checkBox, 0, y++);
910                Location loc = InstanceManager.getDefault(LocationManager.class).getLocationByName(rl.getName());
911                // does the location exist?
912                if (loc != null) {
913                    // need to listen for name and direction changes
914                    loc.removePropertyChangeListener(this);
915                    loc.addPropertyChangeListener(this);
916                    boolean services = false;
917                    // does train direction service location?
918                    if ((rl.getTrainDirection() & loc.getTrainDirections()) != 0) {
919                        services = true;
920                    } // train must service last location or single location
921                    else if (_train.isLocalSwitcher() || rl == _train.getTrainTerminatesRouteLocation()) {
922                        services = true;
923                    }
924                    // check can drop and pick up, and moves > 0
925                    if (services && (rl.isDropAllowed() || rl.isPickUpAllowed()) && rl.getMaxCarMoves() > 0) {
926                        checkBox.setSelected(!_train.isLocationSkipped(rl.getId()));
927                    } else {
928                        checkBox.setEnabled(false);
929                    }
930                    addLocationCheckBoxAction(checkBox);
931                } else {
932                    checkBox.setEnabled(false);
933                }
934            }
935        }
936        locationPanelCheckBoxes.revalidate();
937    }
938
939    private void updateRouteStatus() {
940        Route route = null;
941        textRouteStatus.setText(NONE); // clear out previous status
942        if (_train != null) {
943            route = _train.getRoute();
944        }
945        if (route != null) {
946            if (!route.getStatus().equals(Route.OKAY)) {
947                textRouteStatus.setText(route.getStatus());
948                textRouteStatus.setForeground(Color.RED);
949            }
950        }
951    }
952
953    RouteEditFrame ref;
954    protected static final String NEW_LINE = "\n"; // NOI18N
955
956    private void editAddRoute() {
957        log.debug("Edit/add route");
958        if (ref != null) {
959            ref.dispose();
960        }
961        ref = new RouteEditFrame();
962        setChildFrame(ref);
963        Route route = null;
964        Object selected = routeBox.getSelectedItem();
965        if (selected != null) {
966            route = (Route) selected;
967        }
968        // warn user if train is built that they shouldn't edit the train's route
969        if (route != null && route.getStatus().equals(Route.TRAIN_BUILT)) {
970            // list the built trains for this route
971            StringBuffer buf = new StringBuffer(Bundle.getMessage("DoNotModifyRoute"));
972            for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByIdList()) {
973                if (train.getRoute() == route && train.isBuilt()) {
974                    buf.append(NEW_LINE +
975                            MessageFormat.format(Bundle.getMessage("TrainIsBuilt"),
976                                    new Object[] { train.getName(), route.getName() }));
977                }
978            }
979            JOptionPane.showMessageDialog(this, buf.toString(), Bundle.getMessage("BuiltTrain"),
980                    JOptionPane.WARNING_MESSAGE);
981        }
982        ref.initComponents(route, _train);
983    }
984
985    private void updateDepartureTime() {
986        hourBox.setSelectedItem(_train.getDepartureTimeHour());
987        minuteBox.setSelectedItem(_train.getDepartureTimeMinute());
988        // check to see if route has a departure time from the 1st location
989        RouteLocation rl = _train.getTrainDepartsRouteLocation();
990        if (rl != null && !rl.getDepartureTime().equals(NONE)) {
991            hourBox.setEnabled(false);
992            minuteBox.setEnabled(false);
993        } else {
994            hourBox.setEnabled(true);
995            minuteBox.setEnabled(true);
996        }
997    }
998
999    private void updateRoadAndLoadStatus() {
1000        if (_train != null) {
1001            // road options
1002            if (_train.getCarRoadOption().equals(Train.INCLUDE_ROADS)) {
1003                roadOptionButton.setText(Bundle.getMessage(
1004                        "AcceptOnly") + " " + _train.getCarRoadNames().length + " " + Bundle.getMessage("Roads"));
1005            } else if (_train.getCarRoadOption().equals(Train.EXCLUDE_ROADS)) {
1006                roadOptionButton.setText(Bundle.getMessage(
1007                        "Exclude") + " " + _train.getCarRoadNames().length + " " + Bundle.getMessage("Roads"));
1008            } else if (_train.getLocoRoadOption().equals(Train.INCLUDE_ROADS)) {
1009                roadOptionButton.setText(Bundle.getMessage(
1010                        "AcceptOnly") + " " + _train.getLocoRoadNames().length + " " + Bundle.getMessage("Roads"));
1011            } else if (_train.getLocoRoadOption().equals(Train.EXCLUDE_ROADS)) {
1012                roadOptionButton.setText(Bundle.getMessage(
1013                        "Exclude") + " " + _train.getLocoRoadNames().length + " " + Bundle.getMessage("Roads"));
1014            } else {
1015                roadOptionButton.setText(Bundle.getMessage("AcceptAll"));
1016            }
1017            // load options
1018            if (_train.getLoadOption().equals(Train.ALL_LOADS)) {
1019                loadOptionButton.setText(Bundle.getMessage("AcceptAll"));
1020            } else if (_train.getLoadOption().equals(Train.INCLUDE_LOADS)) {
1021                loadOptionButton.setText(Bundle.getMessage(
1022                        "AcceptOnly") + " " + _train.getLoadNames().length + " " + Bundle.getMessage("Loads"));
1023            } else {
1024                loadOptionButton.setText(Bundle.getMessage(
1025                        "Exclude") + " " + _train.getLoadNames().length + " " + Bundle.getMessage("Loads"));
1026            }
1027            if (!_train.getCarRoadOption().equals(Train.ALL_ROADS) ||
1028                    !_train.getLocoRoadOption().equals(Train.ALL_ROADS) ||
1029                    !_train.getLoadOption().equals(Train.ALL_LOADS)) {
1030                roadAndLoadStatusPanel.setVisible(true);
1031            }
1032        }
1033    }
1034
1035    List<Frame> children = new ArrayList<>();
1036
1037    public void setChildFrame(Frame frame) {
1038        if (children.contains(frame)) {
1039            return;
1040        }
1041        children.add(frame);
1042    }
1043
1044    @Override
1045    public void dispose() {
1046        InstanceManager.getDefault(LocationManager.class).removePropertyChangeListener(this);
1047        InstanceManager.getDefault(EngineTypes.class).removePropertyChangeListener(this);
1048        InstanceManager.getDefault(EngineModels.class).removePropertyChangeListener(this);
1049        InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this);
1050        InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this);
1051        routeManager.removePropertyChangeListener(this);
1052        for (Frame frame : children) {
1053            frame.dispose();
1054        }
1055        if (_train != null) {
1056            _train.removePropertyChangeListener(this);
1057            Route route = _train.getRoute();
1058            if (route != null) {
1059                for (RouteLocation rl : route.getLocationsBySequenceList()) {
1060                    Location loc = rl.getLocation();
1061                    if (loc != null) {
1062                        loc.removePropertyChangeListener(this);
1063                    }
1064                }
1065            }
1066        }
1067        super.dispose();
1068    }
1069
1070    @Override
1071    public void propertyChange(java.beans.PropertyChangeEvent e) {
1072        if (Control.SHOW_PROPERTY) {
1073            log.debug("Property change ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
1074                    e.getNewValue()); // NOI18N
1075        }
1076        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) ||
1077                e.getPropertyName().equals(Train.TYPES_CHANGED_PROPERTY)) {
1078            updateCarTypeCheckboxes();
1079        }
1080        if (e.getPropertyName().equals(EngineTypes.ENGINETYPES_CHANGED_PROPERTY)) {
1081            updateEngineTypeCheckboxes();
1082        }
1083        if (e.getPropertyName().equals(RouteManager.LISTLENGTH_CHANGED_PROPERTY)) {
1084            updateRouteComboBox();
1085        }
1086        if (e.getPropertyName().equals(Route.LISTCHANGE_CHANGED_PROPERTY) ||
1087                e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY) ||
1088                e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY) ||
1089                e.getPropertyName().equals(Location.TRAIN_DIRECTION_CHANGED_PROPERTY)) {
1090            updateLocationCheckboxes();
1091            pack();
1092            repaint();
1093        }
1094        if (e.getPropertyName().equals(CarRoads.CARROADS_CHANGED_PROPERTY)) {
1095            updateRoadComboBoxes();
1096        }
1097        if (e.getPropertyName().equals(EngineModels.ENGINEMODELS_CHANGED_PROPERTY)) {
1098            InstanceManager.getDefault(EngineModels.class).updateComboBox(modelEngineBox);
1099            modelEngineBox.insertItemAt(NONE, 0);
1100            modelEngineBox.setSelectedIndex(0);
1101            if (_train != null) {
1102                modelEngineBox.setSelectedItem(_train.getEngineModel());
1103            }
1104        }
1105        if (e.getPropertyName().equals(Train.DEPARTURETIME_CHANGED_PROPERTY)) {
1106            updateDepartureTime();
1107        }
1108        if (e.getPropertyName().equals(Train.TRAIN_ROUTE_CHANGED_PROPERTY) && _train != null) {
1109            routeBox.setSelectedItem(_train.getRoute());
1110        }
1111        if (e.getPropertyName().equals(Route.ROUTE_STATUS_CHANGED_PROPERTY)) {
1112            enableButtons(_train != null);
1113            updateRouteStatus();
1114        }
1115        if (e.getPropertyName().equals(Train.ROADS_CHANGED_PROPERTY) ||
1116                e.getPropertyName().equals(Train.LOADS_CHANGED_PROPERTY)) {
1117            updateRoadAndLoadStatus();
1118        }
1119    }
1120
1121    private final static Logger log = LoggerFactory.getLogger(TrainEditFrame.class);
1122}