001package jmri.jmrit.display.layoutEditor.LayoutEditorDialogs;
002
003import java.awt.event.ActionListener;
004import java.awt.event.KeyEvent;
005import java.util.ArrayList;
006import java.util.List;
007
008import javax.annotation.Nonnull;
009import javax.swing.*;
010
011import jmri.InstanceManager;
012import jmri.InvokeOnGuiThread;
013import jmri.jmrit.display.layoutEditor.*;
014
015/**
016 * MVC root Editor component for LayoutTrack hierarchy objects.
017 *
018 * @author Bob Jacobsen  Copyright (c) 2020
019 *
020 */
021abstract public class LayoutTrackEditor {
022
023    /**
024     * constructor method.
025     * @param layoutEditor main layout editor.
026     */
027    public LayoutTrackEditor(@Nonnull LayoutEditor layoutEditor) {
028         this.layoutEditor = layoutEditor;
029    }
030
031    // temporary method to get a correct-type *Editor or subclass.
032    // Eventually, this will go away once *Editor's are created
033    // in type-specific *View classes
034    // TODO: should be made not necessary
035    @Nonnull
036    static public LayoutTrackEditor makeTrackEditor(@Nonnull LayoutTrack layoutTrack, @Nonnull LayoutEditor layoutEditor) {
037
038        if (layoutTrack instanceof LayoutTurnout) {
039
040            if (layoutTrack instanceof LayoutRHTurnout) { return new LayoutRHTurnoutEditor(layoutEditor); }
041            if (layoutTrack instanceof LayoutLHTurnout) { return new LayoutLHTurnoutEditor(layoutEditor); }
042            if (layoutTrack instanceof LayoutWye) { return new LayoutWyeEditor(layoutEditor); }
043
044            if (layoutTrack instanceof LayoutXOver) {
045                if (layoutTrack instanceof LayoutRHXOver) { return new LayoutRHXOverEditor(layoutEditor); }
046                if (layoutTrack instanceof LayoutLHXOver) { return new LayoutLHXOverEditor(layoutEditor); }
047                if (layoutTrack instanceof LayoutDoubleXOver) { return new LayoutDoubleXOverEditor(layoutEditor); }
048
049                return new LayoutXOverEditor(layoutEditor);
050            }
051
052            if (layoutTrack instanceof LayoutSlip) {
053                if (layoutTrack instanceof LayoutSingleSlip) { return new LayoutSingleSlipEditor(layoutEditor); }
054                if (layoutTrack instanceof LayoutDoubleSlip) { return new LayoutDoubleSlipEditor(layoutEditor); }
055
056                return new LayoutSlipEditor(layoutEditor);
057            }
058
059            return new LayoutTurnoutEditor(layoutEditor);
060        }
061        if (layoutTrack instanceof TrackSegment) { return new TrackSegmentEditor(layoutEditor); }
062        if (layoutTrack instanceof PositionablePoint) { return new PositionablePointEditor(layoutEditor); }
063        if (layoutTrack instanceof LevelXing) { return new LevelXingEditor(layoutEditor); }
064        if (layoutTrack instanceof LayoutTurntable) { return new LayoutTurntableEditor(layoutEditor); }
065
066        log.error("makeTrackEditor did not match type of {}", layoutTrack, new Exception("traceback"));
067        return new LayoutTrackEditor(layoutEditor){
068            @Override
069            public void editLayoutTrack(@Nonnull LayoutTrackView layoutTrackView) {
070                log.error("Not a valid LayoutTrackEditor implementation", new Exception("traceback"));
071            }
072        };
073    }
074
075    /**
076     * Launch the editor for a particular LayoutTrack-tree object.
077     * @param layoutTrackView the layout track view to edit.
078     */
079    abstract public void editLayoutTrack(@Nonnull LayoutTrackView layoutTrackView);
080
081    final protected LayoutEditor layoutEditor;
082
083    List<String> sensorList = new ArrayList<>();
084
085    protected void addDoneCancelButtons(JPanel target, JRootPane rp, ActionListener doneCallback, ActionListener cancelCallback) {
086        // Done
087        JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));
088        target.add(doneButton);  // NOI18N
089        doneButton.addActionListener(doneCallback);
090        doneButton.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));  // NOI18N
091
092        // Cancel
093        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N
094        target.add(cancelButton);
095        cancelButton.addActionListener(cancelCallback);
096        cancelButton.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));  // NOI18N
097
098        rp.setDefaultButton(doneButton);
099        // bind ESC to close window
100        rp.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
101                KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close"); // NOI18N
102    }
103
104
105    /**
106     * Display a message describing the reason for the block selection combo box
107     * being disabled. An option is provided to hide the message. Note: The
108     * PanelMenu class is being used to satisfy the showInfoMessage requirement
109     * for a default manager type class.
110     *
111     * @since 4.11.2
112     */
113    @InvokeOnGuiThread
114    void showSensorMessage() {
115        if (sensorList.isEmpty()) {
116            return;
117        }
118        StringBuilder msg = new StringBuilder(Bundle.getMessage("BlockSensorLine1"));  // NOI18N
119        msg.append(Bundle.getMessage("BlockSensorLine2"));  // NOI18N
120        String chkDup = "";
121        sensorList.sort(null);
122        for (String sName : sensorList) {
123            if (!sName.equals(chkDup)) {
124                msg.append("<br>&nbsp;&nbsp;&nbsp; " + sName);  // NOI18N
125            }
126            chkDup = sName;
127        }
128        msg.append("<br>&nbsp;</html>");  // NOI18N
129        jmri.InstanceManager.getDefault(jmri.UserPreferencesManager.class).
130                showInfoMessage(
131                        Bundle.getMessage("BlockSensorTitle"), // NOI18N
132                        msg.toString(),
133                        "jmri.jmrit.display.PanelMenu", // NOI18N
134                        "BlockSensorMessage");  // NOI18N
135    }
136
137
138    /**
139     * Create a list of NX sensors that refer to the current layout block. This
140     * is used to disable block selection in the edit dialog. The list is built
141     * by {@link jmri.jmrit.entryexit.EntryExitPairs#layoutBlockSensors}.
142     *
143     * @since 4.11.2
144     * @param loBlk The current layout block.
145     * @return true if sensors are affected.
146     */
147    boolean hasNxSensorPairs(LayoutBlock loBlk) {
148        if (loBlk == null) {
149            return false;
150        }
151        List<String> blockSensors = InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class)
152                .layoutBlockSensors(loBlk);
153        if (blockSensors.isEmpty()) {
154            return false;
155        }
156        sensorList.addAll(blockSensors);
157        return true;
158    }
159
160
161    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTrackEditor.class);
162}