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