001package jmri.jmrit.operations.locations.gui;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005import java.awt.event.MouseEvent;
006import java.util.ArrayList;
007import java.util.List;
008
009import javax.swing.*;
010
011import jmri.*;
012import jmri.jmrit.operations.OperationsFrame;
013import jmri.jmrit.operations.OperationsXml;
014import jmri.jmrit.operations.locations.*;
015import jmri.jmrit.operations.locations.divisions.*;
016import jmri.jmrit.operations.locations.schedules.tools.SchedulesAndStagingAction;
017import jmri.jmrit.operations.locations.tools.*;
018import jmri.jmrit.operations.rollingstock.cars.CarTypes;
019import jmri.jmrit.operations.rollingstock.engines.EngineTypes;
020import jmri.jmrit.operations.routes.Route;
021import jmri.jmrit.operations.routes.RouteManager;
022import jmri.jmrit.operations.routes.tools.ShowRoutesServingLocationAction;
023import jmri.jmrit.operations.routes.tools.ShowRoutesServingLocationFrame;
024import jmri.jmrit.operations.setup.Control;
025import jmri.jmrit.operations.setup.Setup;
026import jmri.jmrit.operations.trains.trainbuilder.TrainCommon;
027import jmri.swing.NamedBeanComboBox;
028import jmri.util.swing.JmriJOptionPane;
029
030/**
031 * Frame for user edit of location
032 *
033 * @author Dan Boudreau Copyright (C) 2008, 2010, 2011, 2012, 2013
034 */
035public class LocationEditFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
036
037    YardTableModel yardModel = new YardTableModel();
038    JTable yardTable = new JTable(yardModel);
039    JScrollPane yardPane = new JScrollPane(yardTable);
040
041    SpurTableModel spurModel = new SpurTableModel();
042    JTable spurTable = new JTable(spurModel) {
043        // create tool tip for Hold column
044        @Override
045        public String getToolTipText(MouseEvent e) {
046            int colIndex = columnAtPoint(e.getPoint());
047            int realColumnIndex = convertColumnIndexToModel(colIndex);
048            if (realColumnIndex == TrackTableModel.QUICK_SERVICE_COLUMN) {
049                return Bundle.getMessage("QuickServiceTip");
050            }
051            if (realColumnIndex == TrackTableModel.HOLD_COLUMN) {
052                return Bundle.getMessage("HoldCarsWithCustomLoads");
053            }
054            return null;
055        }
056    };
057    JScrollPane spurPane = new JScrollPane(spurTable);
058
059    InterchangeTableModel interchangeModel = new InterchangeTableModel();
060    JTable interchangeTable = new JTable(interchangeModel) {
061        // create tool tip for Routed column
062        @Override
063        public String getToolTipText(MouseEvent e) {
064            int colIndex = columnAtPoint(e.getPoint());
065            int realColumnIndex = convertColumnIndexToModel(colIndex);
066            if (realColumnIndex == TrackTableModel.QUICK_SERVICE_COLUMN) {
067                return Bundle.getMessage("QuickServiceTip");
068            }
069            if (realColumnIndex == TrackTableModel.ROUTED_COLUMN) {
070                return Bundle.getMessage("TipOnlyCarsWithFD");
071            }
072            return null;
073        }
074    };
075    JScrollPane interchangePane = new JScrollPane(interchangeTable);
076
077    StagingTableModel stagingModel = new StagingTableModel();
078    JTable stagingTable = new JTable(stagingModel) {
079        // create tool tip for Routed column
080        @Override
081        public String getToolTipText(MouseEvent e) {
082            int colIndex = columnAtPoint(e.getPoint());
083            int realColumnIndex = convertColumnIndexToModel(colIndex);
084            if (realColumnIndex == TrackTableModel.ROUTED_COLUMN) {
085                return Bundle.getMessage("TipOnlyCarsWithFD");
086            }
087            return null;
088        }
089    };
090    JScrollPane stagingPane = new JScrollPane(stagingTable);
091
092    LocationManager locationManager = InstanceManager.getDefault(LocationManager.class);
093    public Location _location = null;
094
095    ArrayList<JCheckBox> checkBoxes = new ArrayList<>();
096    JPanel panelCheckBoxes = new JPanel();
097    JScrollPane typePane = new JScrollPane(panelCheckBoxes);
098
099    JPanel directionPanel = new JPanel();
100
101    // major buttons
102    JButton clearButton = new JButton(Bundle.getMessage("ClearAll"));
103    JButton setButton = new JButton(Bundle.getMessage("SelectAll"));
104    JButton autoSelectButton = new JButton(Bundle.getMessage("AutoSelect"));
105    JButton editDivisionButton = new JButton(Bundle.getMessage("ButtonEdit"));
106    JButton saveLocationButton = new JButton(Bundle.getMessage("SaveLocation"));
107    JButton deleteLocationButton = new JButton(Bundle.getMessage("DeleteLocation"));
108    JButton addLocationButton = new JButton(Bundle.getMessage("AddLocation"));
109    JButton addYardButton = new JButton(Bundle.getMessage("AddYard"));
110    JButton addSpurButton = new JButton(Bundle.getMessage("AddSpur"));
111    JButton addInterchangeButton = new JButton(Bundle.getMessage("AddInterchange"));
112    JButton addStagingButton = new JButton(Bundle.getMessage("AddStaging"));
113
114    // check boxes
115    JCheckBox northCheckBox = new JCheckBox(Bundle.getMessage("North"));
116    JCheckBox southCheckBox = new JCheckBox(Bundle.getMessage("South"));
117    JCheckBox eastCheckBox = new JCheckBox(Bundle.getMessage("East"));
118    JCheckBox westCheckBox = new JCheckBox(Bundle.getMessage("West"));
119
120    // radio buttons
121    JRadioButton stagingRadioButton = new JRadioButton(Bundle.getMessage("StagingOnly"));
122    JRadioButton interchangeRadioButton = new JRadioButton(Bundle.getMessage("Interchange"));
123    JRadioButton yardRadioButton = new JRadioButton(Bundle.getMessage("Yards"));
124    JRadioButton spurRadioButton = new JRadioButton(Bundle.getMessage("Spurs"));
125
126    // text field
127    JTextField locationNameTextField = new JTextField(Control.max_len_string_location_name);
128
129    // text area
130    JTextArea commentTextArea = new JTextArea(4, 60);
131    JScrollPane commentScroller = new JScrollPane(commentTextArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
132            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
133    JColorChooser commentColorChooser = new JColorChooser();
134    JCheckBox boldCheckBox = new JCheckBox();
135
136    // combo boxes
137    protected JComboBox<Division> divisionComboBox = InstanceManager.getDefault(DivisionManager.class).getComboBox();
138
139    // Reader selection dropdown.
140    NamedBeanComboBox<Reporter> readerSelector;
141
142    JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
143
144    public static final String NAME = Bundle.getMessage("Name");
145    public static final int MAX_NAME_LENGTH = Control.max_len_string_location_name;
146    public static final String DISPOSE = "dispose"; // NOI18N
147
148    public LocationEditFrame(Location location) {
149        super(Bundle.getMessage("TitleLocationEdit"));
150
151        _location = location;
152
153        // Set up the jtable in a Scroll Pane..
154        typePane = new JScrollPane(panelCheckBoxes);
155        typePane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
156        typePane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TypesLocation")));
157
158        yardPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
159        yardPane.setBorder(BorderFactory.createTitledBorder(""));
160
161        spurPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
162        spurPane.setBorder(BorderFactory.createTitledBorder(""));
163
164        interchangePane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
165        interchangePane.setBorder(BorderFactory.createTitledBorder(""));
166
167        stagingPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
168        stagingPane.setBorder(BorderFactory.createTitledBorder(""));
169
170        // button group
171        ButtonGroup opsGroup = new ButtonGroup();
172        opsGroup.add(spurRadioButton);
173        opsGroup.add(yardRadioButton);
174        opsGroup.add(interchangeRadioButton);
175        opsGroup.add(stagingRadioButton);
176
177        if (_location != null) {
178            enableButtons(true);
179            locationNameTextField.setText(_location.getName());
180            commentTextArea.setText(TrainCommon.isTextUserModified(_location.getCommentWithColor())
181                    ? _location.getCommentWithColor() : _location.getComment());
182            divisionComboBox.setSelectedItem(_location.getDivision());
183            yardModel.initTable(yardTable, location);
184            spurModel.initTable(spurTable, location);
185            interchangeModel.initTable(interchangeTable, location);
186            stagingModel.initTable(stagingTable, location);
187            _location.addPropertyChangeListener(this);
188            if (!_location.isStaging()) {
189                if (spurModel.getRowCount() > 0) {
190                    spurRadioButton.setSelected(true);
191                } else if (yardModel.getRowCount() > 0) {
192                    yardRadioButton.setSelected(true);
193                } else if (interchangeModel.getRowCount() > 0) {
194                    interchangeRadioButton.setSelected(true);
195                } else {
196                    spurRadioButton.setSelected(true);
197                }
198            } else {
199                stagingRadioButton.setSelected(true);
200            }
201            setTrainDirectionBoxes();
202        } else {
203            enableButtons(false);
204            spurRadioButton.setSelected(true);
205        }
206
207        setVisibleTrackType();
208
209        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
210
211        // Layout the panel by rows
212        // row 1
213        JPanel p1 = new JPanel();
214        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
215        JScrollPane p1Pane = new JScrollPane(p1);
216        p1Pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
217        p1Pane.setMinimumSize(new Dimension(300, 3 * locationNameTextField.getPreferredSize().height));
218        p1Pane.setBorder(BorderFactory.createTitledBorder(""));
219
220        // row 1a
221        JPanel pName = new JPanel();
222        pName.setLayout(new GridBagLayout());
223        pName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Name")));
224
225        addItem(pName, locationNameTextField, 0, 0);
226
227        // row 1b
228        directionPanel.setLayout(new GridBagLayout());
229        directionPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TrainLocation")));
230        addItem(directionPanel, northCheckBox, 1, 0);
231        addItem(directionPanel, southCheckBox, 2, 0);
232        addItem(directionPanel, eastCheckBox, 3, 0);
233        addItem(directionPanel, westCheckBox, 4, 0);
234
235        p1.add(pName);
236        p1.add(directionPanel);
237
238        // division field
239        JPanel pDivision = new JPanel();
240        pDivision.setLayout(new GridBagLayout());
241        pDivision.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Division")));
242        addItem(pDivision, divisionComboBox, 2, 0);
243        addItem(pDivision, editDivisionButton, 3, 0);
244        setDivisionButtonText();
245
246        // row 5
247        panelCheckBoxes.setLayout(new GridBagLayout());
248        updateCheckboxes();
249
250        // row 9
251        JPanel pOp = new JPanel();
252        pOp.setLayout(new GridBagLayout());
253        pOp.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TracksAtLocation")));
254        pOp.add(spurRadioButton);
255        pOp.add(yardRadioButton);
256        pOp.add(interchangeRadioButton);
257        pOp.add(stagingRadioButton);
258
259        // row 11
260        JPanel pC = new JPanel();
261        pC.setLayout(new GridBagLayout());
262        pC.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment")));
263        addItem(pC, commentScroller, 0, 0);
264        if (_location != null) {
265            addItem(pC, getColorChooserPanel(_location.getCommentWithColor(), commentColorChooser, boldCheckBox), 2, 0);
266            boldCheckBox.setSelected(TrainCommon.isTextBold(_location.getCommentWithColor()));
267        } else {
268            addItem(pC, getColorChooserPanel("", commentColorChooser, boldCheckBox), 2, 0);
269        }
270
271        // adjust text area width based on window size less color chooser
272        Dimension d = new Dimension(getPreferredSize().width - 100, getPreferredSize().height);
273        adjustTextAreaColumnWidth(commentScroller, commentTextArea, d);
274
275        JPanel readerPanel = new JPanel();
276        readerPanel.setVisible(false);
277        // reader row
278        if (Setup.isRfidEnabled()) {
279            ReporterManager reporterManager = InstanceManager.getDefault(ReporterManager.class);
280            readerSelector = new NamedBeanComboBox<Reporter>(reporterManager);
281            readerSelector.setAllowNull(true);
282            readerPanel.setLayout(new GridBagLayout());
283            readerPanel.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("idReporter")));
284            addItem(readerPanel, readerSelector, 0, 0);
285            readerPanel.setVisible(true);
286            if (_location != null) {
287                readerSelector.setSelectedItem(_location.getReporter());
288            }
289        }
290
291        // row 12
292        JPanel pB = new JPanel();
293        pB.setLayout(new GridBagLayout());
294        addItem(pB, deleteLocationButton, 0, 0);
295        addItem(pB, addLocationButton, 1, 0);
296        addItem(pB, saveLocationButton, 3, 0);
297
298        getContentPane().add(p1Pane);
299        getContentPane().add(pDivision);
300        getContentPane().add(typePane);
301        getContentPane().add(pOp);
302        getContentPane().add(yardPane);
303        getContentPane().add(addYardButton);
304        getContentPane().add(spurPane);
305        getContentPane().add(addSpurButton);
306        getContentPane().add(interchangePane);
307        getContentPane().add(addInterchangeButton);
308        getContentPane().add(stagingPane);
309        getContentPane().add(addStagingButton);
310        getContentPane().add(pC);
311        getContentPane().add(readerPanel);
312        getContentPane().add(pB);
313
314        // setup buttons
315        addButtonAction(setButton);
316        addButtonAction(clearButton);
317        addButtonAction(autoSelectButton);
318        addButtonAction(editDivisionButton);
319        addButtonAction(deleteLocationButton);
320        addButtonAction(addLocationButton);
321        addButtonAction(saveLocationButton);
322        addButtonAction(addYardButton);
323        addButtonAction(addSpurButton);
324        addButtonAction(addInterchangeButton);
325        addButtonAction(addStagingButton);
326
327        // add tool tips
328        autoSelectButton.setToolTipText(Bundle.getMessage("TipAutoSelect"));
329
330        addRadioButtonAction(spurRadioButton);
331        addRadioButtonAction(yardRadioButton);
332        addRadioButtonAction(interchangeRadioButton);
333        addRadioButtonAction(stagingRadioButton);
334
335        addCheckBoxTrainAction(northCheckBox);
336        addCheckBoxTrainAction(southCheckBox);
337        addCheckBoxTrainAction(eastCheckBox);
338        addCheckBoxTrainAction(westCheckBox);
339
340        addComboBoxAction(divisionComboBox);
341
342        // add property listeners
343        InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this);
344        InstanceManager.getDefault(EngineTypes.class).addPropertyChangeListener(this);
345        InstanceManager.getDefault(DivisionManager.class).addPropertyChangeListener(this);
346        InstanceManager.getDefault(Setup.class).addPropertyChangeListener(this);
347
348        // build menu
349        JMenuBar menuBar = new JMenuBar();
350
351        loadToolMenu();
352        menuBar.add(toolMenu);
353        setJMenuBar(menuBar);
354        addHelpMenu("package.jmri.jmrit.operations.Operations_AddLocation", true); // NOI18N
355
356        initMinimumSize(new Dimension(Control.panelWidth600, Control.panelHeight500));
357    }
358
359    private void loadToolMenu() {
360        toolMenu.removeAll();
361        toolMenu.add(new LocationCopyAction(_location));
362        toolMenu.add(new TrackCopyAction(null, _location));
363        toolMenu.addSeparator();
364        toolMenu.add(new ChangeTracksTypeAction(this));
365        if (_location != null && !_location.isStaging()) {
366            toolMenu.add(new LocationTrackBlockingOrderAction(_location));
367        }
368        toolMenu.add(new ShowTrackMovesAction());
369        toolMenu.addSeparator();
370        toolMenu.add(new EditCarTypeAction());
371        if (Setup.isVsdPhysicalLocationEnabled()) {
372            toolMenu.add(new SetPhysicalLocationAction(_location));
373        }
374        toolMenu.addSeparator();
375        toolMenu.add(new ModifyLocationsAction(_location));
376        toolMenu.add(new ModifyLocationsCarLoadsAction(_location));
377        toolMenu.add(new ModifyLocationsQuickServiceAction(_location));
378        toolMenu.addSeparator();
379        toolMenu.add(new ShowCarsByLocationAction(false, _location, null));
380        toolMenu.add(new ShowLocosByLocationAction(false, _location, null));
381        toolMenu.addSeparator();
382        toolMenu.add(new ShowTrainsServingLocationAction(_location, null));
383        toolMenu.add(new ShowRoutesServingLocationAction(_location));
384        if (_location != null && _location.isStaging()) {
385            toolMenu.add(new SchedulesAndStagingAction());
386        }
387        toolMenu.addSeparator();
388        toolMenu.add(new PrintLocationsAction(false, _location));
389        toolMenu.add(new PrintLocationsAction(true, _location));
390    }
391
392    // frames
393    DivisionEditFrame def = null;
394    YardEditFrame yef = null;
395    SpurEditFrame sef = null;
396    InterchangeEditFrame ief = null;
397    StagingEditFrame stef = null;
398
399    // Save, Delete, Add
400    @Override
401    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
402        if (ae.getSource() == editDivisionButton) {
403            if (def != null) {
404                def.dispose();
405            }
406            def = new DivisionEditFrame((Division) divisionComboBox.getSelectedItem());
407        }
408        if (ae.getSource() == addYardButton) {
409            yef = new YardEditFrame();
410            yef.initComponents(_location, null);
411        }
412        if (ae.getSource() == addSpurButton) {
413            sef = new SpurEditFrame();
414            sef.initComponents(_location, null);
415        }
416        if (ae.getSource() == addInterchangeButton) {
417            ief = new InterchangeEditFrame();
418            ief.initComponents(_location, null);
419        }
420        if (ae.getSource() == addStagingButton) {
421            stef = new StagingEditFrame();
422            stef.initComponents(_location, null);
423        }
424
425        if (ae.getSource() == saveLocationButton) {
426            log.debug("location save button activated");
427            Location l = locationManager.getLocationByName(locationNameTextField.getText());
428            if (_location == null && l == null) {
429                saveNewLocation();
430            } else {
431                if (l != null && l != _location) {
432                    reportLocationExists(Bundle.getMessage("save"));
433                    return;
434                }
435                saveLocation();
436                if (Setup.isCloseWindowOnSaveEnabled()) {
437                    dispose();
438                }
439            }
440        }
441        if (ae.getSource() == deleteLocationButton) {
442            log.debug("location delete button activated");
443            deleteLocation();
444            // save location file
445            OperationsXml.save();
446        }
447        if (ae.getSource() == addLocationButton) {
448            Location l = locationManager.getLocationByName(locationNameTextField.getText());
449            if (l != null) {
450                reportLocationExists(Bundle.getMessage("add"));
451                return;
452            }
453            saveNewLocation();
454        }
455        if (ae.getSource() == setButton) {
456            selectCheckboxes(true);
457        }
458        if (ae.getSource() == clearButton) {
459            selectCheckboxes(false);
460        }
461        if (ae.getSource() == autoSelectButton) {
462            log.debug("auto select button pressed");
463            if (JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("autoSelectCarTypes?"),
464                    Bundle.getMessage("autoSelectLocations?"),
465                    JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) {
466                return;
467            }
468            autoSelectCheckboxes();
469        }
470    }
471
472    private void saveNewLocation() {
473        if (!checkName(Bundle.getMessage("add"))) {
474            return;
475        }
476        Location location = locationManager.newLocation(locationNameTextField.getText());
477        yardModel.initTable(yardTable, location);
478        spurModel.initTable(spurTable, location);
479        interchangeModel.initTable(interchangeTable, location);
480        stagingModel.initTable(stagingTable, location);
481        _location = location;
482        _location.addPropertyChangeListener(this);
483
484        updateCheckboxes();
485        enableButtons(true);
486        setTrainDirectionBoxes();
487        saveLocation();
488        loadToolMenu();
489    }
490
491    private void deleteLocation() {
492        Location location = locationManager.getLocationByName(locationNameTextField.getText());
493        if (location == null) {
494            return;
495        }
496        // check to see if any route uses this location
497        Route route = InstanceManager.getDefault(RouteManager.class).isLocationInUse(location);
498        if (route != null) {
499            JmriJOptionPane.showMessageDialog(this,
500                    Bundle.getMessage("RouteUsesLocation", route.getName(), location.getName()),
501                    Bundle.getMessage("CanNotDeleteLocation"), JmriJOptionPane.ERROR_MESSAGE);
502            // show all the routes using this location
503            ShowRoutesServingLocationFrame frame = new ShowRoutesServingLocationFrame();
504            frame.initComponents(location);
505            return;
506        }
507        int count = location.getNumberRS();
508        if (count > 0) {
509            if (JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("ThereAreCars", Integer.toString(count)),
510                    Bundle.getMessage("deletelocation?"),
511                    JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) {
512                return;
513            }
514        } else {
515            if (JmriJOptionPane.showConfirmDialog(this,
516                    Bundle.getMessage("DoYouWantToDeleteLocation", locationNameTextField.getText()),
517                    Bundle.getMessage("deletelocation?"),
518                    JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) {
519                return;
520            }
521        }
522
523        yardModel.dispose();
524        spurModel.dispose();
525        interchangeModel.dispose();
526        stagingModel.dispose();
527
528        if (yef != null) {
529            yef.dispose();
530        }
531        if (sef != null) {
532            sef.dispose();
533        }
534        if (ief != null) {
535            ief.dispose();
536        }
537        if (stef != null) {
538            stef.dispose();
539        }
540
541        locationManager.deregister(location);
542        _location = null;
543        selectCheckboxes(false);
544        enableCheckboxes(false);
545        enableButtons(false);
546    }
547
548    private void saveLocation() {
549        if (!checkName(Bundle.getMessage("save"))) {
550            return;
551        }
552        // stop table editing so "Moves" are properly saved
553        if (spurTable.isEditing()) {
554            spurTable.getCellEditor().stopCellEditing();
555        }
556        if (yardTable.isEditing()) {
557            yardTable.getCellEditor().stopCellEditing();
558        }
559        if (interchangeTable.isEditing()) {
560            interchangeTable.getCellEditor().stopCellEditing();
561        }
562        if (stagingTable.isEditing()) {
563            stagingTable.getCellEditor().stopCellEditing();
564        }
565        _location.setName(locationNameTextField.getText());
566        _location.setComment(TrainCommon.formatColorString(commentTextArea.getText(), commentColorChooser.getColor(),
567                boldCheckBox.isSelected()));
568        _location.setDivision((Division) divisionComboBox.getSelectedItem());
569        if (Setup.isRfidEnabled() && readerSelector != null) {
570            _location.setReporter(readerSelector.getSelectedItem());
571        }
572        // save location file
573        OperationsXml.save();
574    }
575
576    /**
577     * @return true if name OK and is less than the maximum allowed length
578     */
579    private boolean checkName(String s) {
580        String locationName = locationNameTextField.getText().trim();
581        if (locationName.isEmpty()) {
582            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("MustEnterName"),
583                    Bundle.getMessage("CanNotLocation", s), JmriJOptionPane.ERROR_MESSAGE);
584            return false;
585        }
586        // hyphen feature needs at least one character to work properly
587        if (locationName.contains(TrainCommon.HYPHEN)) {
588            String[] check = locationName.split(TrainCommon.HYPHEN);
589            if (check.length == 0) {
590                JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("HyphenFeature"),
591                        Bundle.getMessage("CanNotLocation", s), JmriJOptionPane.ERROR_MESSAGE);
592                return false;
593            }
594            locationName = check[0];
595        }
596        if (TrainCommon.splitString(locationName).length() > MAX_NAME_LENGTH) {
597            // log.error("Location name must be less than "+
598            // Integer.toString(MAX_NAME_LENGTH+1) +" characters");
599            JmriJOptionPane.showMessageDialog(this,
600                    Bundle.getMessage("LocationNameLengthMax", Integer.toString(MAX_NAME_LENGTH + 1)),
601                    Bundle.getMessage("CanNotLocation", s), JmriJOptionPane.ERROR_MESSAGE);
602            return false;
603        }
604        if (!OperationsXml.checkFileName(locationName)) { // NOI18N
605            JmriJOptionPane.showMessageDialog(this,
606                    Bundle.getMessage("NameResChar") + NEW_LINE + Bundle.getMessage("ReservedChar"),
607                    Bundle.getMessage("CanNotLocation", s), JmriJOptionPane.ERROR_MESSAGE);
608            return false;
609        }
610        return true;
611    }
612
613    private void reportLocationExists(String s) {
614        JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("LocationAlreadyExists"),
615                Bundle.getMessage("CanNotLocation", s), JmriJOptionPane.ERROR_MESSAGE);
616    }
617
618    private void enableButtons(boolean enabled) {
619        toolMenu.setEnabled(enabled);
620        northCheckBox.setEnabled(enabled);
621        southCheckBox.setEnabled(enabled);
622        eastCheckBox.setEnabled(enabled);
623        westCheckBox.setEnabled(enabled);
624        divisionComboBox.setEnabled(enabled);
625        editDivisionButton.setEnabled(enabled);
626        clearButton.setEnabled(enabled);
627        setButton.setEnabled(enabled);
628        autoSelectButton.setEnabled(enabled);
629        addYardButton.setEnabled(enabled);
630        addSpurButton.setEnabled(enabled);
631        addInterchangeButton.setEnabled(enabled);
632        addStagingButton.setEnabled(enabled);
633        saveLocationButton.setEnabled(enabled);
634        deleteLocationButton.setEnabled(enabled);
635        // the inverse!
636        addLocationButton.setEnabled(!enabled);
637        // enable radio buttons
638        spurRadioButton.setEnabled(enabled);
639        yardRadioButton.setEnabled(enabled);
640        interchangeRadioButton.setEnabled(enabled);
641        stagingRadioButton.setEnabled(enabled);
642        if (readerSelector != null) {
643            // enable readerSelect.
644            readerSelector.setEnabled(enabled && Setup.isRfidEnabled());
645        }
646    }
647
648    @Override
649    public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) {
650        setVisibleTrackType();
651    }
652
653    private void setVisibleTrackType() {
654        enableTrackTypeRadioButtons();
655        interchangePane.setVisible(interchangeRadioButton.isSelected());
656        addInterchangeButton.setVisible(interchangeRadioButton.isSelected());
657        stagingPane.setVisible(stagingRadioButton.isSelected());
658        addStagingButton.setVisible(stagingRadioButton.isSelected());
659        yardPane.setVisible(yardRadioButton.isSelected());
660        addYardButton.setVisible(yardRadioButton.isSelected());
661        spurPane.setVisible(spurRadioButton.isSelected());
662        addSpurButton.setVisible(spurRadioButton.isSelected());
663    }
664
665    private void enableTrackTypeRadioButtons() {
666        if (spurModel.getRowCount() > 0 || yardModel.getRowCount() > 0 || interchangeModel.getRowCount() > 0) {
667            if (stagingRadioButton.isSelected()) {
668                spurRadioButton.setSelected(true);
669            }
670            stagingRadioButton.setEnabled(false);
671        } else if (stagingModel.getRowCount() > 0) {
672            stagingRadioButton.setSelected(true);
673            spurRadioButton.setEnabled(false);
674            yardRadioButton.setEnabled(false);
675            interchangeRadioButton.setEnabled(false);
676        } else if (_location != null) {
677            spurRadioButton.setEnabled(true);
678            yardRadioButton.setEnabled(true);
679            interchangeRadioButton.setEnabled(true);
680            stagingRadioButton.setEnabled(true);
681        }
682    }
683
684    private void enableCheckboxes(boolean enable) {
685        for (JCheckBox checkBox : new ArrayList<>(checkBoxes)) {
686            checkBox.setEnabled(enable);
687        }
688    }
689
690    /*
691     * Protected against concurrent changes by making a copy of the checkBoxes
692     * list.
693     */
694    private void selectCheckboxes(boolean select) {
695        for (JCheckBox checkBox : new ArrayList<>(checkBoxes)) {
696            checkBox.setSelected(select);
697            if (_location != null) {
698                if (select) {
699                    _location.addTypeName(checkBox.getText());
700                } else {
701                    _location.deleteTypeName(checkBox.getText());
702                }
703            }
704        }
705    }
706
707    private void updateCheckboxes() {
708        x = 0;
709        y = 0;
710        checkBoxes.clear();
711        panelCheckBoxes.removeAll();
712        numberOfCheckBoxes = getNumberOfCheckboxesPerLine();
713        loadTypes(InstanceManager.getDefault(CarTypes.class).getNames());
714
715        // add space between car and loco types
716        checkNewLine();
717
718        loadTypes(InstanceManager.getDefault(EngineTypes.class).getNames());
719        JPanel p = new JPanel();
720        p.add(clearButton);
721        p.add(setButton);
722        p.add(autoSelectButton);
723        GridBagConstraints gc = new GridBagConstraints();
724        gc.gridwidth = getNumberOfCheckboxesPerLine() + 1;
725        gc.gridy = ++y;
726        panelCheckBoxes.add(p, gc);
727        panelCheckBoxes.revalidate();
728        repaint();
729    }
730
731    protected void updateDivisionComboBox() {
732        InstanceManager.getDefault(DivisionManager.class).updateComboBox(divisionComboBox);
733        if (_location != null) {
734            divisionComboBox.setSelectedItem(_location.getDivision());
735        }
736    }
737
738    int x = 0;
739    int y = 0; // vertical position in panel
740
741    private void loadTypes(String[] types) {
742        for (String type : types) {
743            JCheckBox checkBox = new JCheckBox();
744            checkBoxes.add(checkBox);
745            checkBox.setText(type);
746            addCheckBoxAction(checkBox);
747            addItemLeft(panelCheckBoxes, checkBox, x, y);
748            if (_location != null) {
749                if (_location.acceptsTypeName(type)) {
750                    checkBox.setSelected(true);
751                }
752            } else {
753                checkBox.setEnabled(false);
754            }
755            checkNewLine();
756        }
757    }
758
759    int numberOfCheckBoxes;
760
761    private void checkNewLine() {
762        if (++x > numberOfCheckBoxes) {
763            y++;
764            x = 0;
765        }
766    }
767
768    /**
769     * Adjust the location's car service types to only reflect the car types
770     * serviced by the location's tracks. Protected against concurrent changes
771     * by creating a new list of checkboxes.
772     */
773    private void autoSelectCheckboxes() {
774        for (JCheckBox checkBox : new ArrayList<>(checkBoxes)) {
775            checkBox.setSelected(false);
776            // check each track to determine which car types are serviced by
777            // this location
778            List<Track> tracks = _location.getTracksList();
779            for (Track track : tracks) {
780                if (track.isTypeNameAccepted(checkBox.getText())) {
781                    checkBox.setSelected(true);
782                }
783            }
784            // this type of car isn't serviced by any of the tracks, so delete
785            if (!checkBox.isSelected()) {
786                _location.deleteTypeName(checkBox.getText());
787            }
788        }
789    }
790
791    LocationsByCarTypeFrame lctf = null;
792
793    @Override
794    public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) {
795        JCheckBox b = (JCheckBox) ae.getSource();
796        log.debug("checkbox change {}", b.getText());
797        if (_location == null) {
798            return;
799        }
800        _location.removePropertyChangeListener(this);
801        if (b.isSelected()) {
802            _location.addTypeName(b.getText());
803            // show which tracks will service this car type
804            if (InstanceManager.getDefault(CarTypes.class).containsName(b.getText())) {
805                if (lctf != null) {
806                    lctf.dispose();
807                }
808                lctf = new LocationsByCarTypeFrame();
809                lctf.initComponents(_location, b.getText());
810            }
811        } else {
812            _location.deleteTypeName(b.getText());
813        }
814        _location.addPropertyChangeListener(this);
815    }
816
817    private void addCheckBoxTrainAction(JCheckBox b) {
818        b.addActionListener(new java.awt.event.ActionListener() {
819            @Override
820            public void actionPerformed(java.awt.event.ActionEvent e) {
821                checkBoxActionTrainPerformed(e);
822            }
823        });
824    }
825
826    private void checkBoxActionTrainPerformed(java.awt.event.ActionEvent ae) {
827        // save train directions serviced by this location
828        if (_location == null) {
829            return;
830        }
831        int direction = 0;
832        if (northCheckBox.isSelected()) {
833            direction += Location.NORTH;
834        }
835        if (southCheckBox.isSelected()) {
836            direction += Location.SOUTH;
837        }
838        if (eastCheckBox.isSelected()) {
839            direction += Location.EAST;
840        }
841        if (westCheckBox.isSelected()) {
842            direction += Location.WEST;
843        }
844        _location.setTrainDirections(direction);
845
846    }
847
848    private void setTrainDirectionBoxes() {
849        northCheckBox.setVisible((Setup.getTrainDirection() & Setup.NORTH) == Setup.NORTH);
850        southCheckBox.setVisible((Setup.getTrainDirection() & Setup.SOUTH) == Setup.SOUTH);
851        eastCheckBox.setVisible((Setup.getTrainDirection() & Setup.EAST) == Setup.EAST);
852        westCheckBox.setVisible((Setup.getTrainDirection() & Setup.WEST) == Setup.WEST);
853
854        northCheckBox.setSelected((_location.getTrainDirections() & Location.NORTH) == Location.NORTH);
855        southCheckBox.setSelected((_location.getTrainDirections() & Location.SOUTH) == Location.SOUTH);
856        eastCheckBox.setSelected((_location.getTrainDirections() & Location.EAST) == Location.EAST);
857        westCheckBox.setSelected((_location.getTrainDirections() & Location.WEST) == Location.WEST);
858    }
859
860    @Override
861    protected void comboBoxActionPerformed(ActionEvent ae) {
862        setDivisionButtonText();
863    }
864
865    private void setDivisionButtonText() {
866        if (divisionComboBox.getSelectedItem() == null) {
867            editDivisionButton.setText(Bundle.getMessage("Add"));
868        } else {
869            editDivisionButton.setText(Bundle.getMessage("ButtonEdit"));
870        }
871    }
872
873    @Override
874    public void dispose() {
875        if (_location != null) {
876            _location.removePropertyChangeListener(this);
877        }
878        InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this);
879        InstanceManager.getDefault(EngineTypes.class).removePropertyChangeListener(this);
880        yardModel.dispose();
881        spurModel.dispose();
882        interchangeModel.dispose();
883        stagingModel.dispose();
884        if (lctf != null) {
885            lctf.dispose();
886        }
887        if (yef != null) {
888            yef.dispose();
889        }
890        if (sef != null) {
891            sef.dispose();
892        }
893        if (ief != null) {
894            ief.dispose();
895        }
896        if (stef != null) {
897            stef.dispose();
898        }
899        super.dispose();
900    }
901
902    @Override
903    public void propertyChange(java.beans.PropertyChangeEvent e) {
904        if (Control.SHOW_PROPERTY) {
905            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(),
906                    e.getNewValue());
907        }
908        if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) ||
909                e.getPropertyName().equals(EngineTypes.ENGINETYPES_CHANGED_PROPERTY) ||
910                e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY)) {
911            updateCheckboxes();
912        }
913        if (e.getPropertyName().equals(DivisionManager.LISTLENGTH_CHANGED_PROPERTY)) {
914            updateDivisionComboBox();
915        }
916        if (e.getPropertyName().equals(Setup.TRAIN_DIRECTION_PROPERTY_CHANGE)) {
917            setTrainDirectionBoxes();
918        }
919    }
920
921    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LocationEditFrame.class);
922}