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