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