001package jmri.jmrit.display.controlPanelEditor;
002
003import java.awt.Component;
004import java.awt.Dimension;
005import java.awt.FlowLayout;
006import java.beans.PropertyChangeEvent;
007import java.beans.PropertyChangeListener;
008import java.awt.event.ActionEvent;
009import java.util.ArrayList;
010import java.util.Iterator;
011import java.util.stream.Collectors;
012
013import javax.swing.AbstractListModel;
014import javax.swing.Box;
015import javax.swing.BoxLayout;
016import javax.swing.JButton;
017import javax.swing.JComponent;
018import javax.swing.JLabel;
019import javax.swing.JList;
020import javax.swing.JPanel;
021import javax.swing.JScrollPane;
022import javax.swing.JTextField;
023import javax.swing.ListCellRenderer;
024import javax.swing.event.ListSelectionEvent;
025import javax.swing.event.ListSelectionListener;
026
027import jmri.BeanSetting;
028import jmri.Path;
029import jmri.Sensor;
030import jmri.Turnout;
031import jmri.jmrit.display.IndicatorTrack;
032import jmri.jmrit.display.IndicatorTurnoutIcon;
033import jmri.jmrit.display.Positionable;
034import jmri.jmrit.logix.OBlock;
035import jmri.jmrit.logix.OPath;
036import jmri.jmrit.logix.Portal;
037import jmri.util.swing.JmriJOptionPane;
038
039/**
040 * @author Pete Cressman Copyright: Copyright (c) 2011
041 *
042 */
043public class EditCircuitPaths extends EditFrame implements ListSelectionListener {
044
045    // mouse selections of track icons that define the path
046    private ArrayList<Positionable> _pathGroup = new ArrayList<>();
047    private ArrayList<Positionable> _savePathGroup = new ArrayList<>();
048
049    private JTextField _pathName;
050    private JList<OPath> _pathList;
051    private PathListModel _pathListModel;
052    private OPath _currentPath;
053
054    private LengthPanel _lengthPanel;
055    public static final String TEST_PATH = "TEST_PATH";
056
057    public EditCircuitPaths(String title, CircuitBuilder parent, OBlock block) {
058        super(title, parent, block);
059        checkCircuitIcons("BlockPaths");
060        pack();
061    }
062
063    @Override
064    protected JPanel makeContentPanel() {
065        JPanel pathPanel = new JPanel();
066        pathPanel.setLayout(new BoxLayout(pathPanel, BoxLayout.Y_AXIS));
067
068        pathPanel.add(Box.createVerticalStrut(STRUT_SIZE));
069        JPanel panel = new JPanel();
070        panel.add(new JLabel(Bundle.getMessage("PathTitle", _homeBlock.getDisplayName())));
071        pathPanel.add(panel);
072
073        _pathListModel = new PathListModel(this);
074        _pathList = new JList<>();
075        _pathList.setModel(_pathListModel);
076        _pathList.addListSelectionListener(this);
077        _homeBlock.addPropertyChangeListener(_pathListModel);
078        
079        _pathList.setCellRenderer(new PathCellRenderer());
080        _pathList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
081        pathPanel.add(new JScrollPane(_pathList));
082        pathPanel.add(Box.createVerticalStrut(STRUT_SIZE));
083
084        panel = new JPanel();
085        panel.setLayout(new FlowLayout());
086
087        JButton clearButton = new JButton(Bundle.getMessage("buttonClearSelection"));
088        clearButton.addActionListener((ActionEvent a) -> clearListSelection());
089        clearButton.setToolTipText(Bundle.getMessage("ToolTipClearList"));
090        panel.add(clearButton);
091        pathPanel.add(panel);
092        pathPanel.add(Box.createVerticalStrut(STRUT_SIZE));
093
094        panel = new JPanel();
095        _pathName = new JTextField();
096        panel.add(CircuitBuilder.makeTextBoxPanel(
097                false, _pathName, "pathName", true, "TooltipPathName"));
098        _pathName.setPreferredSize(new Dimension(300, _pathName.getPreferredSize().height));
099        pathPanel.add(panel);
100
101        panel = new JPanel();
102        JButton addButton = new JButton(Bundle.getMessage("buttonAddPath"));
103        addButton.addActionListener((ActionEvent a) -> addNewPath(true));
104        addButton.setToolTipText(Bundle.getMessage("ToolTipAddPath"));
105        panel.add(addButton);
106
107        JButton changeButton = new JButton(Bundle.getMessage("buttonChangeName"));
108        changeButton.addActionListener((ActionEvent a) -> changePathName());
109        changeButton.setToolTipText(Bundle.getMessage("ToolTipChangeName"));
110        panel.add(changeButton);
111
112        JButton deleteButton = new JButton(Bundle.getMessage("buttonDeletePath"));
113        deleteButton.addActionListener((ActionEvent a) -> deletePath());
114        deleteButton.setToolTipText(Bundle.getMessage("ToolTipDeletePath"));
115        panel.add(deleteButton);
116
117        pathPanel.add(panel);
118        pathPanel.add(Box.createVerticalStrut(STRUT_SIZE));
119
120        _lengthPanel = new LengthPanel(_homeBlock, LengthPanel.PATH_LENGTH, "TooltipPathLength");
121        pathPanel.add(_lengthPanel);
122        pathPanel.add(Box.createVerticalStrut(STRUT_SIZE));
123
124        panel = new JPanel();
125        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
126        JLabel l = new JLabel(Bundle.getMessage("enterNewPath"));
127        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
128        panel.add(l);
129        l = new JLabel(Bundle.getMessage("selectPathIcons"));
130        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
131        panel.add(l);
132        l = new JLabel(Bundle.getMessage("pressAddButton", Bundle.getMessage("buttonAddPath")));
133        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
134        panel.add(l);
135        panel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
136        l = new JLabel(Bundle.getMessage("selectPath"));
137        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
138        panel.add(l);
139        l = new JLabel(Bundle.getMessage("editPathIcons"));
140        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
141        panel.add(l);
142        panel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
143        l = new JLabel(Bundle.getMessage("throwPathTO"));
144        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
145        panel.add(l);
146        l = new JLabel(Bundle.getMessage("holdShiftDown"));
147        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
148        panel.add(l);
149        JPanel p = new JPanel();
150        p.add(panel);
151        pathPanel.add(p);
152
153        pathPanel.add(Box.createVerticalStrut(STRUT_SIZE));
154        pathPanel.add(makeDoneButtonPanel());
155        _lengthPanel.changeUnits();
156        return pathPanel;
157    }
158
159    private static class PathCellRenderer extends JLabel implements ListCellRenderer<OPath> {
160
161        @Override
162        public Component getListCellRendererComponent(
163                JList<? extends OPath> list, // the list
164                OPath value, // value to display
165                int index, // cell index
166                boolean isSelected, // is the cell selected
167                boolean cellHasFocus) // does the cell have focus
168        {
169            String s = value.getDescription();
170            setText(s);
171            if (isSelected) {
172                setBackground(list.getSelectionBackground());
173                setForeground(list.getSelectionForeground());
174            } else {
175                setBackground(list.getBackground());
176                setForeground(list.getForeground());
177            }
178            setEnabled(list.isEnabled());
179            setFont(list.getFont());
180            setOpaque(true);
181            return this;
182        }
183    }
184
185    class PathListModel extends AbstractListModel<OPath> implements PropertyChangeListener {
186
187        EditFrame _parent;
188
189        PathListModel(EditFrame parent) {
190            _parent = parent;
191        }
192
193        @Override
194        public int getSize() {
195            return _homeBlock.getPaths().size();
196        }
197
198        @Override
199        public OPath getElementAt(int index) {
200            return (OPath) _homeBlock.getPaths().get(index);
201        }
202
203        public void dataChange() {
204            fireContentsChanged(this, 0, 0);
205        }
206
207        @Override
208        public void propertyChange(PropertyChangeEvent e) {
209            if (e.getPropertyName().equals("deleted")) {
210                _parent.closingEvent(true);
211            }
212            fireContentsChanged(this, 0, 0);
213        }
214    }
215
216    @Override
217    public void valueChanged(ListSelectionEvent e) {
218        OPath path = _pathList.getSelectedValue();
219        if (log.isDebugEnabled()) {
220            log.debug("valueChanged from _currentPath \"{}\" to path \"{}\"",
221                    (_currentPath==null?"null":_currentPath.getName()), (path==null?"null":path.getName()));
222        }
223        String msg = checkForSavePath();
224        if (msg.length() > 0) {
225            StringBuilder  sb = new StringBuilder (msg);
226            sb.append("\n");
227            sb.append(Bundle.getMessage("saveChanges"));
228            int answer = JmriJOptionPane.showConfirmDialog(this, sb.toString(), Bundle.getMessage("makePath"),
229                    JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE);
230            if (answer == JmriJOptionPane.YES_OPTION) {
231                addNewPath(false);
232            }
233        }
234        clearPath(false);
235        _currentPath = path;
236        if (path != null) {
237            _pathName.setText(path.getName());
238            _lengthPanel.setLength(path.getLengthMm());
239            _pathGroup = showPath(path);
240            updatePath();
241        } else {
242            _pathName.setText(null);
243            _lengthPanel.setLength(0);
244        }
245        int oldState = _homeBlock.getState();
246        int newState = oldState | OBlock.ALLOCATED;
247        _homeBlock.pseudoPropertyChange("state", oldState, newState);
248    }
249
250    private ArrayList<Positionable> showPath(OPath path) {
251        if (log.isDebugEnabled()) {
252            log.debug("showPath  \"{}\"", path.getName());
253        }
254        path.setTurnouts(0, true, 0, false);
255        ArrayList<Positionable> pathGp = makePathGroup(path);
256        _savePathGroup = new ArrayList<>();
257        for (Positionable pos :pathGp) {
258            _savePathGroup.add(pos);
259        }
260        return pathGp;
261    }
262
263    /**
264     * Construct the array of icons that displays the path
265     * <p>
266     */
267    private ArrayList<Positionable> makePathGroup(OPath path) {
268        Portal fromPortal = path.getFromPortal();
269        Portal toPortal = path.getToPortal();
270        String name = path.getName();
271
272        java.util.List<Positionable> list = _parent.getCircuitIcons(_homeBlock);
273        ArrayList<Positionable> pathGroup = new ArrayList<>();
274        for (Positionable pos : list) {
275            if (pos instanceof IndicatorTrack) {
276                ArrayList<String> paths = ((IndicatorTrack) pos).getPaths();
277                if (paths != null) {
278                    for (String s : paths) {
279                        if (name.equals(s)) {
280                            ((IndicatorTrack) pos).setControlling(true);
281                            pathGroup.add(pos);
282                        }
283                    }
284                }
285            } else if (pos instanceof PortalIcon) {
286                PortalIcon icon = (PortalIcon) pos;
287                Portal portal = icon.getPortal();
288                if (portal.equals(fromPortal)) {
289                    pathGroup.add(icon);
290                } else if (portal.equals(toPortal)) {
291                    pathGroup.add(icon);
292                }
293            }
294        }
295        if (log.isDebugEnabled()) {
296            log.debug("makePathGroup for path \"{}\" from CircuitGroup size {} pathGroup size {}", name, list.size(), pathGroup.size());
297        }
298        return pathGroup;
299    }
300
301    /**
302     * Can a path in this circuit be drawn through this icon?
303     */
304    private boolean okPath(Positionable pos) {
305        if (pos instanceof PortalIcon) {
306            Portal portal = ((PortalIcon) pos).getPortal();
307            if (portal != null) {
308                if (_homeBlock.equals(portal.getFromBlock()) || _homeBlock.equals(portal.getToBlock())) {
309                    ((PortalIcon) pos).setStatus(PortalIcon.PATH);
310                    return true;
311                }
312            }
313            JmriJOptionPane.showMessageDialog(this, java.text.MessageFormat.format(
314                    Bundle.getMessage("portalNotInCircuit"), _homeBlock.getDisplayName()),
315                    Bundle.getMessage("badPath"), JmriJOptionPane.WARNING_MESSAGE);
316            return false;
317        }
318        java.util.List<Positionable> icons = _parent.getCircuitIcons(_homeBlock);
319        if (!icons.contains(pos)) {
320            JmriJOptionPane.showMessageDialog(this, java.text.MessageFormat.format(
321                    Bundle.getMessage("iconNotInCircuit"), _homeBlock.getDisplayName()),
322                    Bundle.getMessage("badPath"), JmriJOptionPane.WARNING_MESSAGE);
323            return false;
324        }
325        return true;
326    }
327
328    /*
329     * CircuitBuilder calls from handleSelection to update icon display
330     */
331    protected void updateSelections(boolean noShift, Positionable selection) {
332        // A temporary path "TEST_PATH" is used to display the icons representing a path
333        // the OBlock has allocated TEST_PATH
334        // pathGroup collects the icons and the actual path is edited or
335        // created with a save in _editPathsFrame
336        if (!canEdit()) {
337            return;
338        }
339        if (noShift) {
340            if (_pathGroup.contains(selection)) {
341                _pathGroup.remove(selection);
342                if (selection instanceof PortalIcon) {
343                    ((PortalIcon) selection).setStatus(PortalIcon.VISIBLE);
344                } else {
345                    ((IndicatorTrack) selection).setStatus(Sensor.INACTIVE);
346                    ((IndicatorTrack) selection).removePath(TEST_PATH);
347                    log.debug("removePath TEST_PATH");
348                }
349            } else if (okPath(selection)) {
350                _pathGroup.add(selection);
351                // okPath() sets PortalIcons to status PortalIcon.PATH
352                if (selection instanceof IndicatorTrack) {
353                    ((IndicatorTrack) selection).addPath(TEST_PATH);
354                }
355            } else {
356                return;
357            }
358        } else {
359            if (selection instanceof PortalIcon) {
360                ((PortalIcon) selection).setStatus(PortalIcon.VISIBLE);
361            }
362        }
363        int oldState = _homeBlock.getState();
364        int newState = oldState | OBlock.ALLOCATED;
365        _homeBlock.pseudoPropertyChange("state", oldState, newState);
366        log.debug("updateSelections ALLOCATED _homeBlock");
367    }
368    /**
369     * Set the path icons for display.
370     */
371    private void updatePath() {
372        // to avoid ConcurrentModificationException now set data
373        for (Positionable pos : _pathGroup) {
374            if (pos instanceof IndicatorTrack) {
375                ((IndicatorTrack) pos).addPath(TEST_PATH);
376            } else {
377                ((PortalIcon) pos).setStatus(PortalIcon.PATH);
378            }
379        }
380        String name = _pathName.getText();
381        if (!_pathGroup.isEmpty() && (name == null || name.length() == 0)) {
382            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("needPathName"),
383                    Bundle.getMessage("makePath"), JmriJOptionPane.INFORMATION_MESSAGE);
384        }
385    }
386
387    private String findErrors() {
388        StringBuilder  sb = new StringBuilder();
389        java.util.List<Path> list = _homeBlock.getPaths();
390        if (list.isEmpty()) {
391            sb.append(Bundle.getMessage("noPaths", _homeBlock.getDisplayName()));
392        } else {
393            list.stream().filter(o -> o instanceof OPath)
394                    .map(o->(OPath) o).forEach( path -> {
395                ArrayList<Positionable> pathGp = makePathGroup(path);
396                if (pathGp.isEmpty()) {
397                    sb.append(Bundle.getMessage("noPathIcons", path.getName()));
398                    sb.append("\n");
399                } else {
400                    String msg = checkIcons(path.getName(), pathGp);
401                    if (msg != null) {
402                        sb.append(msg);
403                        sb.append("\n");
404                    }
405                }
406            });
407        }
408        return sb.toString();
409    }
410
411    private boolean pathIconsEqual(ArrayList<Positionable> pathGp1, ArrayList<Positionable> pathGp2) {
412        if (pathGp1.size() != pathGp2.size()) {
413            return false;
414        }
415        for (Positionable pos : pathGp1) {
416            if (!pathGp2.contains(pos)) {
417                return false;
418            }
419        }
420        return true;
421    }
422
423    /**
424     * Checks if icons of path are different
425     */
426    private String checkForSavePath() {
427        String name = _pathName.getText();
428        StringBuilder  sb = new StringBuilder();
429        if (_currentPath != null) {
430            String curName = _currentPath.getName();
431            if (!pathIconsEqual(_pathGroup, _savePathGroup)) {
432                sb.append(Bundle.getMessage("pathIconsChanged", curName));
433                sb.append("\n");
434            }
435            if (_lengthPanel.isChanged(_currentPath.getLengthMm())) {
436                sb.append(Bundle.getMessage("pathlengthChanged", curName));
437                sb.append("\n");
438            }
439            if (name.length() > 0 && !name.equals(_currentPath.getName())) {
440                sb.append(Bundle.getMessage("changeName", name, curName));
441                sb.append("\n");
442            }
443        }
444        return sb.toString();
445    }
446
447    //////////////////////////// end setup ////////////////////////////
448    @Override
449    protected void clearListSelection() {
450        _pathList.clearSelection();
451        _lengthPanel.setLength(0);
452        _pathName.setText(null);
453    }
454
455    private String checkIcons(String name, ArrayList<Positionable> pathGp) {
456        Iterator<Positionable> it = pathGp.iterator();
457        boolean hasTrack = false;
458        boolean hasPortal = false;
459        while (it.hasNext()) {
460            Positionable pos = it.next();
461            if (pos instanceof IndicatorTrack) {
462                hasTrack = true;
463            } else if (pos instanceof PortalIcon) {
464                hasPortal = true;
465            }
466        }
467        if (!hasTrack) {
468            return Bundle.getMessage("noTrackIconsForPath", name);
469        } else if (!hasPortal) {
470            return Bundle.getMessage("noPortalIconsForPath", name);
471        }
472        return null;
473    }
474
475    /**
476     * Make the OPath from the icons in the Iterator
477     */
478    private OPath makeOPath(String name, ArrayList<Positionable> pathGp) {
479        if (pathGp.isEmpty()) {
480            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("noPathIcons", name),
481                    Bundle.getMessage("makePath"), JmriJOptionPane.INFORMATION_MESSAGE);
482            return null;
483        }
484        Iterator<Positionable> it = pathGp.iterator();
485        ArrayList<BeanSetting> settings = new ArrayList<>();
486        Portal fromPortal = null;
487        Portal toPortal = null;
488        boolean hasTrack = false;
489        int portalIconCount = 0;
490        StringBuilder  sb = new StringBuilder ();
491        while (it.hasNext()) {
492            Positionable pos = it.next();
493            if (pos instanceof IndicatorTurnoutIcon) {
494                jmri.Turnout t = ((IndicatorTurnoutIcon) pos).getTurnout();
495                String turnoutName = t.getDisplayName();
496                int state = t.getKnownState();
497                if (state != Turnout.CLOSED && state != Turnout.THROWN) {
498                    if (sb.length() > 0) {
499                        sb.append("\n");
500                    }
501                    sb.append(Bundle.getMessage("turnoutNotSet", turnoutName));
502                } else {
503                    settings.add(new BeanSetting(t, turnoutName, state));
504                    hasTrack = true;
505                }
506            } else if (pos instanceof PortalIcon) {
507                if (toPortal == null) {
508                    toPortal = ((PortalIcon) pos).getPortal();
509                } else if (fromPortal == null) {
510                    fromPortal = ((PortalIcon) pos).getPortal();
511                }
512                portalIconCount++;
513            } else if (pos instanceof IndicatorTrack) {
514                hasTrack = true;
515            }
516        }
517        if (!hasTrack) {
518            if (sb.length() > 0) {
519                sb.append("\n");
520            }
521            sb.append(Bundle.getMessage("noTrackIconsForPath", name));
522        }
523        if (toPortal == null && fromPortal == null) {
524            if (sb.length() > 0) {
525                sb.append("\n");
526            }
527            sb.append(Bundle.getMessage("tooFewPortals"));
528        }
529        if (portalIconCount == 0) {
530            if (sb.length() > 0) {
531                sb.append("\n");
532            }
533            sb.append(Bundle.getMessage("noPortalIconsForPath", name));
534        }
535        if (portalIconCount > 2) {
536            if (sb.length() > 0) {
537                sb.append("\n");
538            }
539            sb.append(Bundle.getMessage("tooManyPortals"));
540        }
541        if (sb.length() > 0) {
542            JmriJOptionPane.showMessageDialog(this, sb.toString(),
543                    Bundle.getMessage("makePath"), JmriJOptionPane.INFORMATION_MESSAGE);
544            return null;
545        }
546
547        if (log.isDebugEnabled()) {
548            log.debug("makeOPath for path \"{}\" from {} icons", name, pathGp.size());
549        }
550        return new OPath(name, _homeBlock, fromPortal, toPortal, settings);
551    }
552
553    /**
554     * Create or update the selected path named in the text field Checks that
555     * icons have been selected for the path
556     */
557    private void addNewPath(boolean prompt) {
558        String name = _pathName.getText();
559        if (log.isDebugEnabled()) {
560            log.debug("addPath({}) for path \"{}\"", prompt, name);
561        }
562        if (name == null || name.trim().length() == 0) {
563            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("TooltipPathName"),
564                    Bundle.getMessage("makePath"), JmriJOptionPane.INFORMATION_MESSAGE);
565            return;
566        }
567        OPath otherPath = null; 
568        OPath newPath = makeOPath(name, _pathGroup);
569        if (newPath == null) {
570            return;
571        }
572        // is this path already defined? OPath equality is equal turnout settings and portals, not icons
573        for (OPath loopPath : _homeBlock.getPaths().stream()
574                .filter(o -> o instanceof OPath).map(o -> (OPath)o)
575                .collect(Collectors.toList())) {
576            if (newPath.equals(loopPath)) {
577                otherPath = loopPath;
578                break;
579            }
580        }
581        if (log.isDebugEnabled()) {
582            log.debug("newPath= {}", newPath.toString());
583            log.debug("otherPath= {}", (otherPath==null?"null":otherPath.toString()));
584            log.debug("_currentPath= {}", (_currentPath==null?"null":_currentPath.toString()));
585            log.debug("current path {} changed", (pathIconsEqual(_pathGroup, _savePathGroup)? "not" : "IS"));
586        }
587
588        if (otherPath != null && !otherPath.equals(_currentPath)) {
589            ArrayList<Positionable> otherPathGrp = makePathGroup(otherPath);
590            String otherName = otherPath.getName();
591            StringBuilder  sb = new StringBuilder (Bundle.getMessage("pathDefined", otherName));
592            sb.append("\n");
593            if (name.length() > 0 && !name.equals(otherName)) {
594                sb.append(Bundle.getMessage("changeName", name, otherName));
595                sb.append("\n");
596            }
597            if (!pathIconsEqual(_pathGroup, otherPathGrp)) {
598                sb.append(Bundle.getMessage("pathIconsChanged", otherName));
599                sb.append("\n");
600            }
601            if (_lengthPanel.isChanged(otherPath.getLengthMm())) {
602                sb.append(Bundle.getMessage("pathlengthChanged", otherName));
603                sb.append("\n");
604            }
605            if (sb.length() > 0 && prompt) {
606                sb.append(Bundle.getMessage("saveChanges"));
607                int result = JmriJOptionPane.showConfirmDialog(this, sb.toString(),
608                        Bundle.getMessage("makePath"), JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE);
609                if (result == JmriJOptionPane.YES_OPTION) {
610                    _currentPath = otherPath;
611                }
612            } else {
613                return;
614            }
615        }
616        
617        // match icons to current selections
618        changePathNameInIcons(name, _pathGroup);
619
620        if (_currentPath != null) {
621            _currentPath.setName(name);
622            Portal toPortal = newPath.getToPortal();
623            toPortal.addPath(_currentPath);
624            Portal fromPortal = newPath.getFromPortal();
625            if (fromPortal != null) {
626                fromPortal.addPath(_currentPath);
627            }
628            _currentPath.setToPortal(toPortal);
629            _currentPath.setFromPortal(fromPortal);
630            _currentPath.setLength(_lengthPanel.getLength());
631            _currentPath.clearSettings();
632            for (BeanSetting beanSetting : newPath.getSettings()) {
633                _currentPath.addSetting(beanSetting);
634            }
635            _savePathGroup = _pathGroup;
636            log.debug("update _currentPath");
637        } else {
638            newPath.setLength(_lengthPanel.getLength());
639            _homeBlock.addPath(newPath);  // OBlock adds path to portals and checks for duplicate path names
640            log.debug("add newPath");
641        }
642    }
643
644    private void changePathName() {
645        String name = _pathName.getText();
646        if (name == null || name.trim().length() == 0 || _currentPath == null) {
647            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("changePathName", Bundle.getMessage("buttonChangeName")),
648                    Bundle.getMessage("makePath"), JmriJOptionPane.INFORMATION_MESSAGE);
649            return;
650        }
651        OPath aPath = _homeBlock.getPathByName(name);
652        if (aPath != null) {
653            if (name.equals(_currentPath.getName())) {
654                _pathList.setSelectedValue(aPath, true);
655                return;
656            }
657            JmriJOptionPane.showMessageDialog(this, 
658                    Bundle.getMessage("duplicatePathName", name, _homeBlock.getDisplayName()),
659                    Bundle.getMessage("makePath"), JmriJOptionPane.INFORMATION_MESSAGE);
660            clearPath(false);
661            _pathName.setText(null);
662            return;
663        }
664        _currentPath.setName(name);     // sends propertyChange to track icons
665        if (!pathIconsEqual(_pathGroup, _savePathGroup)) {
666            StringBuilder  sb = new StringBuilder ();
667            sb.append(Bundle.getMessage("pathIconsChanged", name));
668            sb.append("\n");
669            sb.append(Bundle.getMessage("saveIcons"));
670            int result = JmriJOptionPane.showConfirmDialog(this, sb.toString(),
671                    Bundle.getMessage("makePath"), JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE);
672            if (result == JmriJOptionPane.YES_OPTION) {
673                changePathNameInIcons(name, _pathGroup);
674                _savePathGroup = _pathGroup;
675            } else {
676                changePathNameInIcons(name, _savePathGroup);
677                clearPath(false);
678                _pathGroup = _savePathGroup;
679                updatePath();
680            }
681        } else {
682            changePathNameInIcons(name, _pathGroup);
683        }
684        _pathList.setSelectedValue(_currentPath, true);
685    }
686
687    private void changePathNameInIcons(String name, ArrayList<Positionable> pathGp) {
688        log.debug("changePathNameInIcons for {}.  {} icons", name, pathGp.size());
689        // add or remove path name from IndicatorTrack icons
690        for (Positionable pos : _parent.getCircuitIcons(_homeBlock)) {
691            if (pathGp.contains(pos)) {
692                if (pos instanceof IndicatorTrack) {
693                    ((IndicatorTrack) pos).addPath(name);
694                }
695            } else {
696                if (pos instanceof IndicatorTrack) {
697                    ((IndicatorTrack) pos).removePath(name);
698                }
699            }
700        }
701    }
702
703    private void deletePath() {
704        OPath path = _pathList.getSelectedValue();
705        if (path == null) {
706            // check that name was typed in and not selected
707            path = _homeBlock.getPathByName(_pathName.getText());
708        }
709        if (path == null) {
710            return;
711        }
712        if (_homeBlock.removeOPath(path)) {
713            clearListSelection();
714            _pathListModel.dataChange();
715        }
716    }
717
718    @Override
719    protected void closingEvent(boolean close) {
720        StringBuilder  sb = new StringBuilder ();
721        String msg = checkForSavePath();
722        if(msg.length() > 0) {
723            sb.append(msg);
724            sb.append("\n");
725        }
726        msg = findErrors();
727        if (msg.length() > 0) {
728            sb.append(msg);
729        }
730        if (closingEvent(close, sb.toString())) {
731            _pathName.setText(null);
732            clearPath(true);
733            int oldState = _homeBlock.getState();
734            int newState = oldState | OBlock.ALLOCATED;
735            _homeBlock.pseudoPropertyChange("state", oldState, newState);
736            _homeBlock.removePropertyChangeListener(_pathListModel);
737        }// else...  Don't clear current selections, if continuing to edit
738    }
739
740    private void clearPath(boolean hidePortals) {
741        if (_pathGroup != null) {
742            log.debug("clearPath deAllocate _pathGroup with {} icons", _pathGroup.size());
743            for (Positionable pos : _pathGroup) {
744                if (pos instanceof PortalIcon) {
745                    PortalIcon pi = (PortalIcon) pos;
746                    if (hidePortals) {
747                        pi.setStatus(PortalIcon.HIDDEN);
748                    } else {
749                        pi.setStatus(PortalIcon.VISIBLE);
750                    }
751                } else if (pos instanceof IndicatorTrack) {
752                    ((IndicatorTrack)pos).removePath(TEST_PATH);
753                }
754            }
755            _pathGroup.clear();
756            int oldState = _homeBlock.getState();
757            int newState = oldState & ~OBlock.ALLOCATED;
758            _homeBlock.pseudoPropertyChange("state", oldState, newState);
759            _currentPath = null;
760        } else {
761            log.debug("clearPath pathGroup null");
762        }
763    }
764
765    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EditCircuitPaths.class);
766
767}