001package jmri.jmrit.display.layoutEditor;
002
003import java.awt.BorderLayout;
004import java.awt.Color;
005import java.awt.Component;
006import java.awt.Container;
007import java.awt.FlowLayout;
008import java.awt.GridLayout;
009import java.awt.Point;
010import java.awt.event.ActionEvent;
011import java.awt.event.WindowAdapter;
012import java.awt.event.WindowEvent;
013import java.awt.geom.Point2D;
014
015import java.util.*;
016import javax.annotation.*;
017import javax.swing.*;
018import javax.swing.border.Border;
019
020import jmri.*;
021import jmri.NamedBean.DisplayOptions;
022import jmri.implementation.DefaultConditionalAction;
023import jmri.jmrit.blockboss.BlockBossLogic;
024import jmri.jmrit.blockboss.BlockBossLogicProvider;
025import jmri.jmrit.catalog.NamedIcon;
026import jmri.jmrit.display.*;
027import jmri.jmrit.signalling.SignallingGuiTools;
028import jmri.swing.NamedBeanComboBox;
029import jmri.util.JmriJFrame;
030import jmri.util.MathUtil;
031import jmri.util.swing.JComboBoxUtil;
032import jmri.util.swing.JmriJOptionPane;
033
034/**
035 * Layout Editor Tools provides tools making use of layout connectivity
036 * available in Layout Editor panels.
037 * <p>
038 * The tools in this module are accessed via the Tools menu in Layout Editor.
039 *
040 * @author Dave Duchamp Copyright (c) 2007
041 * @author George Warner Copyright (c) 2017-2019
042 */
043public final class LayoutEditorTools {
044
045    //constants
046    //private final int NONE = 0;  //Signal at Turnout Positions
047    //operational instance variables shared between tools
048    private LayoutEditor layoutEditor = null;
049    private MultiIconEditor signalIconEditor = null;
050    private JFrame signalFrame = null;
051    private boolean needRedraw = false;
052    private BlockBossLogic logic = null;
053    private SignalHead auxSignal = null;
054
055    //constructor method
056    public LayoutEditorTools(@Nonnull LayoutEditor thePanel) {
057        layoutEditor = thePanel;
058
059        //Turnouts
060        LayoutEditor.setupComboBox(sensorsTurnoutComboBox, true, true, false);
061        LayoutEditor.setupComboBox(signalMastsTurnoutComboBox, true, true, false);
062        LayoutEditor.setupComboBox(turnout1ComboBox, true, true, false);
063        LayoutEditor.setupComboBox(turnout2ComboBox, true, true, false);
064        LayoutEditor.setupComboBox(turnoutAComboBox, true, true, false);
065        LayoutEditor.setupComboBox(turnoutBComboBox, true, true, false);
066        LayoutEditor.setupComboBox(turnoutComboBox, true, true, false);
067
068        //Blocks
069        LayoutEditor.setupComboBox(block1IDComboBox, true, true, false);
070        LayoutEditor.setupComboBox(block2IDComboBox, true, true, false);
071        LayoutEditor.setupComboBox(blockACComboBox, true, true, false);
072        LayoutEditor.setupComboBox(blockBDComboBox, true, true, false);
073        LayoutEditor.setupComboBox(slipSensorsBlockAComboBox, true, true, false);
074        LayoutEditor.setupComboBox(slipSensorsBlockBComboBox, true, true, false);
075        LayoutEditor.setupComboBox(slipSensorsBlockCComboBox, true, true, false);
076        LayoutEditor.setupComboBox(slipSensorsBlockDComboBox, true, true, false);
077        LayoutEditor.setupComboBox(slipSignalBlockAComboBox, true, true, false);
078        LayoutEditor.setupComboBox(slipSignalBlockBComboBox, true, true, false);
079        LayoutEditor.setupComboBox(slipSignalBlockCComboBox, true, true, false);
080        LayoutEditor.setupComboBox(slipSignalBlockDComboBox, true, true, false);
081        LayoutEditor.setupComboBox(xingBlockACComboBox, true, true, false);
082        LayoutEditor.setupComboBox(xingBlockBDComboBox, true, true, false);
083        LayoutEditor.setupComboBox(xingSensorsBlockACComboBox, true, true, false);
084        LayoutEditor.setupComboBox(xingSensorsBlockBDComboBox, true, true, false);
085
086        //Signal Heads
087        LayoutEditor.setupComboBox(a1_3WaySignalHeadComboBox, true, true, false);
088        LayoutEditor.setupComboBox(a1SignalHeadComboBox, true, true, false);
089        LayoutEditor.setupComboBox(a1SlipSignalHeadComboBox, true, true, false);
090        LayoutEditor.setupComboBox(a1TToTSignalHeadComboBox, true, true, false);
091        LayoutEditor.setupComboBox(a2_3WaySignalHeadComboBox, true, true, false);
092        LayoutEditor.setupComboBox(a2SignalHeadComboBox, true, true, false);
093        LayoutEditor.setupComboBox(a2SlipSignalHeadComboBox, true, true, false);
094        LayoutEditor.setupComboBox(a2TToTSignalHeadComboBox, true, true, false);
095        LayoutEditor.setupComboBox(a3_3WaySignalHeadComboBox, true, true, false);
096        LayoutEditor.setupComboBox(aSignalHeadComboBox, true, true, false);
097        LayoutEditor.setupComboBox(b_3WaySignalHeadComboBox, true, true, false);
098        LayoutEditor.setupComboBox(b1SignalHeadComboBox, true, true, false);
099        LayoutEditor.setupComboBox(b1SlipSignalHeadComboBox, true, true, false);
100        LayoutEditor.setupComboBox(b1TToTSignalHeadComboBox, true, true, false);
101        LayoutEditor.setupComboBox(b2SignalHeadComboBox, true, true, false);
102        LayoutEditor.setupComboBox(b2SlipSignalHeadComboBox, true, true, false);
103        LayoutEditor.setupComboBox(b2TToTSignalHeadComboBox, true, true, false);
104        LayoutEditor.setupComboBox(bSignalHeadComboBox, true, true, false);
105        LayoutEditor.setupComboBox(c_3WaySignalHeadComboBox, true, true, false);
106        LayoutEditor.setupComboBox(c1SignalHeadComboBox, true, true, false);
107        LayoutEditor.setupComboBox(c1SlipSignalHeadComboBox, true, true, false);
108        LayoutEditor.setupComboBox(c1TToTSignalHeadComboBox, true, true, false);
109        LayoutEditor.setupComboBox(c2SignalHeadComboBox, true, true, false);
110        LayoutEditor.setupComboBox(c2SlipSignalHeadComboBox, true, true, false);
111        LayoutEditor.setupComboBox(c2TToTSignalHeadComboBox, true, true, false);
112        LayoutEditor.setupComboBox(continuingSignalHeadComboBox, false, true, false);
113        LayoutEditor.setupComboBox(cSignalHeadComboBox, true, true, false);
114        LayoutEditor.setupComboBox(d_3WaySignalHeadComboBox, true, true, false);
115        LayoutEditor.setupComboBox(d1SignalHeadComboBox, true, true, false);
116        LayoutEditor.setupComboBox(d1SlipSignalHeadComboBox, true, true, false);
117        LayoutEditor.setupComboBox(d1TToTSignalHeadComboBox, true, true, false);
118        LayoutEditor.setupComboBox(d2SignalHeadComboBox, true, true, false);
119        LayoutEditor.setupComboBox(d2SlipSignalHeadComboBox, true, true, false);
120        LayoutEditor.setupComboBox(d2TToTSignalHeadComboBox, true, true, false);
121        LayoutEditor.setupComboBox(divergingSignalHeadComboBox, false, true, false);
122        LayoutEditor.setupComboBox(dSignalHeadComboBox, true, true, false);
123        LayoutEditor.setupComboBox(eastBoundSignalHeadComboBox, true, true, false);
124        LayoutEditor.setupComboBox(throatContinuingSignalHeadComboBox, false, true, false);
125        LayoutEditor.setupComboBox(throatDivergingSignalHeadComboBox, false, true, false);
126        LayoutEditor.setupComboBox(westBoundSignalHeadComboBox, true, true, false);
127
128        // TODO: Set combobox exclude lists for turnouts, blocks and signal heads
129        // that are not part of the current layout panel
130    }
131
132    /*=====================*\
133    |* setSignalsAtTurnout *|
134    \*=====================*/
135    /**
136     * Tool to set signals at a turnout, including placing the signal icons and
137     * optionally setup of Simple Signal Logic for each signal head
138     * <p>
139     * This tool assumes left facing signal head icons have been selected, and
140     * will rotate the signal head icons accordingly.
141     * <p>
142     * This tool will place throat icons on the right side of the track, and
143     * continuing and diverging icons on the outside edge of the turnout.
144     */
145    //operational variables for Set Signals at Turnout tool
146    private JmriJFrame setSignalsAtTurnoutFrame = null;
147    private boolean setSignalsAtTurnoutOpenFlag = false;
148    private boolean setSignalsAtTurnoutFromMenuFlag = false;
149
150    private JLabel turnoutNameLabel = null;
151
152    private final NamedBeanComboBox<Turnout> turnoutComboBox = new NamedBeanComboBox<>(
153            InstanceManager.turnoutManagerInstance(),
154            null, DisplayOptions.DISPLAYNAME);
155
156    private final NamedBeanComboBox<SignalHead> throatContinuingSignalHeadComboBox = new NamedBeanComboBox<>(
157            InstanceManager.getDefault(SignalHeadManager.class),
158            null, DisplayOptions.DISPLAYNAME);
159    private final NamedBeanComboBox<SignalHead> throatDivergingSignalHeadComboBox = new NamedBeanComboBox<>(
160            InstanceManager.getDefault(SignalHeadManager.class),
161            null, DisplayOptions.DISPLAYNAME);
162    private final NamedBeanComboBox<SignalHead> continuingSignalHeadComboBox = new NamedBeanComboBox<>(
163            InstanceManager.getDefault(SignalHeadManager.class),
164            null, DisplayOptions.DISPLAYNAME);
165    private final NamedBeanComboBox<SignalHead> divergingSignalHeadComboBox = new NamedBeanComboBox<>(
166            InstanceManager.getDefault(SignalHeadManager.class),
167            null, DisplayOptions.DISPLAYNAME);
168
169    private final JCheckBox setPlaceAllHeads = new JCheckBox(Bundle.getMessage("PlaceAllHeads"));
170    private final JCheckBox setupAllLogic = new JCheckBox(Bundle.getMessage("SetAllLogic"));
171
172    private final JCheckBox setThroatContinuing = new JCheckBox(Bundle.getMessage("PlaceHead"));
173    private final JCheckBox setupLogicThroatContinuing = new JCheckBox(Bundle.getMessage("SetLogic"));
174    private final JCheckBox setThroatDiverging = new JCheckBox(Bundle.getMessage("PlaceHead"));
175    private final JCheckBox setupLogicThroatDiverging = new JCheckBox(Bundle.getMessage("SetLogic"));
176    private final JCheckBox setContinuing = new JCheckBox(Bundle.getMessage("PlaceHead"));
177    private final JCheckBox setupLogicContinuing = new JCheckBox(Bundle.getMessage("SetLogic"));
178    private final JCheckBox setDiverging = new JCheckBox(Bundle.getMessage("PlaceHead"));
179    private final JCheckBox setupLogicDiverging = new JCheckBox(Bundle.getMessage("SetLogic"));
180    private JButton getSavedSignalHeads = null;
181    private JButton changeSignalIcon = null;
182    private JButton setSignalsDone = null;
183    private JButton setSignalsCancel = null;
184
185    private LayoutTurnout layoutTurnout = null;
186    private double placeSignalDirectionDEG = 0.0;
187
188    private Turnout turnout = null;
189    private SignalHead throatContinuingHead = null;
190    private SignalHead throatDivergingHead = null;
191    private SignalHead continuingHead = null;
192    private SignalHead divergingHead = null;
193
194    //display dialog for Set Signals at Turnout tool
195    public void setSignalsAtTurnoutFromMenu(@Nonnull LayoutTurnout to,
196            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
197        layoutTurnout = to;
198        turnout = to.getTurnout();
199        turnoutComboBox.setSelectedItem(to.getTurnout());
200        setSignalsAtTurnoutFromMenuFlag = true;
201        setSignalsAtTurnout(theEditor, theFrame);
202        setSignalsAtTurnoutFromMenuFlag = false;
203    }
204
205    public void setSignalsAtTurnout(@Nonnull MultiIconEditor theEditor,
206            @Nonnull JFrame theFrame) {
207        signalIconEditor = theEditor;
208        signalFrame = theFrame;
209
210        //Initialize if needed
211        if (setSignalsAtTurnoutFrame == null) {
212            setSignalsAtTurnoutOpenFlag = false;
213            setSignalsAtTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalsAtTurnout"), false, true);
214            oneFrameToRuleThemAll(setSignalsAtTurnoutFrame);
215            setSignalsAtTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
216
217            setSignalsAtTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtTurnout", true);
218            setSignalsAtTurnoutFrame.setLocation(70, 30);
219            Container theContentPane = setSignalsAtTurnoutFrame.getContentPane();
220            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
221
222            JPanel panel1 = new JPanel(new FlowLayout());
223            turnoutNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout")));
224            panel1.add(turnoutNameLabel);
225            panel1.add(turnoutComboBox);
226            turnoutNameLabel.setLabelFor(turnoutComboBox);
227            turnoutComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
228            theContentPane.add(panel1);
229            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
230
231            JPanel panel2 = new JPanel(new FlowLayout());
232            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
233            panel2.add(shTitle);
234            panel2.add(new JLabel("   "));
235            panel2.add(getSavedSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
236            getSavedSignalHeads.addActionListener(this::turnoutSignalsGetSaved);
237            getSavedSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
238            theContentPane.add(panel2);
239
240            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
241            JPanel panel2a = new JPanel(new FlowLayout());
242            panel2a.add(new JLabel("   "));
243            panel2a.add(setPlaceAllHeads);
244            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
245            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
246                boolean isSelected = setPlaceAllHeads.isSelected();
247                //(de)select all checkboxes
248                setThroatContinuing.setSelected(isSelected);
249                setThroatDiverging.setSelected(isSelected);
250                setContinuing.setSelected(isSelected);
251                setDiverging.setSelected(isSelected);
252            });
253            panel2a.add(new JLabel("  "));
254            panel2a.add(setupAllLogic);
255            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
256            setupAllLogic.addActionListener((ActionEvent e) -> {
257                boolean isSelected = setupAllLogic.isSelected();
258                //(de)select all checkboxes
259                setupLogicThroatContinuing.setSelected(isSelected);
260                setupLogicThroatDiverging.setSelected(isSelected);
261                setupLogicContinuing.setSelected(isSelected);
262                setupLogicDiverging.setSelected(isSelected);
263            });
264            theContentPane.add(panel2a);
265
266            JPanel panel21 = new JPanel(new FlowLayout());
267            JLabel throatContinuingLabel = new JLabel(
268                    Bundle.getMessage("MakeLabel", throatContinuingString));
269            panel21.add(throatContinuingLabel);
270            panel21.add(throatContinuingSignalHeadComboBox);
271            throatContinuingLabel.setLabelFor(throatContinuingSignalHeadComboBox);
272            theContentPane.add(panel21);
273            throatContinuingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
274
275            JPanel panel22 = new JPanel(new FlowLayout());
276            panel22.add(new JLabel("   "));
277            panel22.add(setThroatContinuing);
278            setThroatContinuing.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
279            panel22.add(new JLabel("  "));
280            panel22.add(setupLogicThroatContinuing);
281            setupLogicThroatContinuing.setToolTipText(Bundle.getMessage("SetLogicHint"));
282            theContentPane.add(panel22);
283
284            JPanel panel31 = new JPanel(new FlowLayout());
285            JLabel throatDivergingLabel = new JLabel(
286                    Bundle.getMessage("MakeLabel", throatDivergingString));
287            panel31.add(throatDivergingLabel);
288            panel31.add(throatDivergingSignalHeadComboBox);
289            throatDivergingLabel.setLabelFor(throatDivergingSignalHeadComboBox);
290            theContentPane.add(panel31);
291            throatDivergingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
292
293            JPanel panel32 = new JPanel(new FlowLayout());
294            panel32.add(new JLabel("   "));
295            panel32.add(setThroatDiverging);
296            setThroatDiverging.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
297            panel32.add(new JLabel("  "));
298            panel32.add(setupLogicThroatDiverging);
299            setupLogicThroatDiverging.setToolTipText(Bundle.getMessage("SetLogicHint"));
300            theContentPane.add(panel32);
301
302            JPanel panel41 = new JPanel(new FlowLayout());
303            JLabel continuingLabel = new JLabel(
304                    Bundle.getMessage("MakeLabel", continuingString));
305            panel41.add(continuingLabel);
306            panel41.add(continuingSignalHeadComboBox);
307            continuingLabel.setLabelFor(continuingSignalHeadComboBox);
308            theContentPane.add(panel41);
309            continuingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
310
311            JPanel panel42 = new JPanel(new FlowLayout());
312            panel42.add(new JLabel("   "));
313            panel42.add(setContinuing);
314            setContinuing.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
315            panel42.add(new JLabel("  "));
316            panel42.add(setupLogicContinuing);
317            setupLogicContinuing.setToolTipText(Bundle.getMessage("SetLogicHint"));
318            theContentPane.add(panel42);
319
320            JPanel panel51 = new JPanel(new FlowLayout());
321            JLabel divergingLabel = new JLabel(
322                    Bundle.getMessage("MakeLabel", divergingString));
323            panel51.add(divergingLabel);
324            panel51.add(divergingSignalHeadComboBox);
325            divergingLabel.setLabelFor(divergingSignalHeadComboBox);
326            theContentPane.add(panel51);
327            divergingSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
328
329            JPanel panel52 = new JPanel(new FlowLayout());
330            panel52.add(new JLabel("   "));
331            panel52.add(setDiverging);
332            setDiverging.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
333            panel52.add(new JLabel("  "));
334            panel52.add(setupLogicDiverging);
335            setupLogicDiverging.setToolTipText(Bundle.getMessage("SetLogicHint"));
336            theContentPane.add(panel52);
337            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
338
339            JPanel panel6 = new JPanel(new FlowLayout());
340            panel6.add(changeSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
341            changeSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
342            changeSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
343            panel6.add(new JLabel("   "));
344            panel6.add(setSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
345            setSignalsDone.addActionListener(this::setSignalsDonePressed);
346            setSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
347
348            panel6.add(setSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
349            setSignalsCancel.addActionListener(this::setSignalsCancelPressed);
350            setSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
351            theContentPane.add(panel6);
352            setSignalsAtTurnoutFrame.addWindowListener(new WindowAdapter() {
353                @Override
354                public void windowClosing(WindowEvent e) {
355                    setSignalsCancelPressed(null);
356                }
357            });
358        }
359        setPlaceAllHeads.setSelected(false);
360        setupAllLogic.setSelected(false);
361
362        turnoutComboBox.setVisible(!setSignalsAtTurnoutFromMenuFlag);
363        String turnoutLabelString = Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout"));
364        if (setSignalsAtTurnoutFromMenuFlag) {
365            turnoutNameLabel.setText(turnoutLabelString + layoutTurnout.getTurnoutName());
366            turnoutSignalsGetSaved(null);
367        } else {
368            turnoutNameLabel.setText(turnoutLabelString);
369        }
370
371        if (!setSignalsAtTurnoutOpenFlag) {
372            setSignalsAtTurnoutFrame.setPreferredSize(null);
373            setSignalsAtTurnoutFrame.pack();
374            setSignalsAtTurnoutOpenFlag = true;
375        }
376        setSignalsAtTurnoutFrame.setVisible(true);
377    }   //setSignalsAtTurnout
378
379    private void turnoutSignalsGetSaved(ActionEvent a) {
380        if (getTurnoutInformation(false)) {
381            throatContinuingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA1());
382            throatDivergingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA2());
383            continuingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalB1());
384            divergingSignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalC1());
385        }
386    }
387
388    private void setSignalsCancelPressed(ActionEvent a) {
389        setSignalsAtTurnoutOpenFlag = false;
390        setSignalsAtTurnoutFrame.setVisible(false);
391    }
392
393    private void setSignalsDonePressed(ActionEvent a) {
394        //process turnout name
395        if (!getTurnoutInformation(false)) {
396            return;
397        }
398        //process signal head names
399        if (!getTurnoutSignalHeadInformation()) {
400            return;
401        }
402        //place signals as requested
403        String signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
404        if (signalHeadName == null) {
405            signalHeadName = "";
406        }
407        if (setThroatContinuing.isSelected()) {
408            if (isHeadOnPanel(throatContinuingHead)
409                    && (throatContinuingHead != getHeadFromName(layoutTurnout.getSignalA1Name()))) {
410                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
411                        Bundle.getMessage("SignalsError6",
412                                new Object[]{signalHeadName}),
413                        Bundle.getMessage("ErrorTitle"),
414                        JmriJOptionPane.ERROR_MESSAGE);
415                return;
416            } else {
417                removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
418                placeThroatContinuing();
419                removeAssignment(throatContinuingHead);
420                layoutTurnout.setSignalA1Name(signalHeadName);
421                needRedraw = true;
422            }
423        } else {
424            LayoutTurnout.Geometry assigned = isHeadAssignedHere(throatContinuingHead, layoutTurnout);
425            if (assigned == LayoutTurnout.Geometry.NONE) {
426                if (isHeadOnPanel(throatContinuingHead)
427                        && isHeadAssignedAnywhere(throatContinuingHead)) {
428                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
429                            Bundle.getMessage("SignalsError8",
430                                    new Object[]{signalHeadName}),
431                            Bundle.getMessage("ErrorTitle"),
432                            JmriJOptionPane.ERROR_MESSAGE);
433                    return;
434                } else {
435                    removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
436                    removeAssignment(throatContinuingHead);
437                    layoutTurnout.setSignalA1Name(signalHeadName);
438                }
439                //} else if (assigned != A1) {
440                //TODO: need to figure out what to do in this case
441                //assigned to a different position on the same turnout.
442                //}
443            }
444        }
445        signalHeadName = throatDivergingSignalHeadComboBox.getSelectedItemDisplayName();
446        if (signalHeadName == null) {
447            signalHeadName = "";
448        }
449        if ((setThroatDiverging.isSelected()) && (throatDivergingHead != null)) {
450            if (isHeadOnPanel(throatDivergingHead)
451                    && (throatDivergingHead != getHeadFromName(layoutTurnout.getSignalA2Name()))) {
452                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
453                        Bundle.getMessage("SignalsError6",
454                                new Object[]{signalHeadName}),
455                        Bundle.getMessage("ErrorTitle"),
456                        JmriJOptionPane.ERROR_MESSAGE);
457                return;
458            } else {
459                removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
460                placeThroatDiverging();
461                removeAssignment(throatDivergingHead);
462                layoutTurnout.setSignalA2Name(signalHeadName);
463                needRedraw = true;
464            }
465        } else if (throatDivergingHead != null) {
466            LayoutTurnout.Geometry assigned = isHeadAssignedHere(throatDivergingHead, layoutTurnout);
467            if (assigned == LayoutTurnout.Geometry.NONE) {
468                if (isHeadOnPanel(throatDivergingHead)
469                        && isHeadAssignedAnywhere(throatDivergingHead)) {
470                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
471                            Bundle.getMessage("SignalsError8",
472                                    new Object[]{signalHeadName}),
473                            Bundle.getMessage("ErrorTitle"),
474                            JmriJOptionPane.ERROR_MESSAGE);
475                    return;
476                } else {
477                    removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
478                    removeAssignment(throatDivergingHead);
479                    layoutTurnout.setSignalA2Name(signalHeadName);
480                }
481                //} else if (assigned != A2) {
482                //need to figure out what to do in this case - assigned to a different position on the same turnout.
483            }
484        } else {   //throatDivergingHead is always null here
485            removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
486            layoutTurnout.setSignalA2Name("");
487        }
488
489        signalHeadName = continuingSignalHeadComboBox.getSelectedItemDisplayName();
490        if (signalHeadName == null) {
491            signalHeadName = "";
492        }
493        if (setContinuing.isSelected()) {
494            if (isHeadOnPanel(continuingHead)
495                    && (continuingHead != getHeadFromName(layoutTurnout.getSignalB1Name()))) {
496                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
497                        Bundle.getMessage("SignalsError6",
498                                new Object[]{signalHeadName}),
499                        Bundle.getMessage("ErrorTitle"),
500                        JmriJOptionPane.ERROR_MESSAGE);
501                return;
502            } else {
503                removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
504                if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
505                    placeContinuing(signalHeadName);
506                } else {
507                    placeDiverging(signalHeadName);
508                }
509                removeAssignment(continuingHead);
510                layoutTurnout.setSignalB1Name(signalHeadName);
511                needRedraw = true;
512            }
513        } else {
514            LayoutTurnout.Geometry assigned = isHeadAssignedHere(continuingHead, layoutTurnout);
515            if (assigned == LayoutTurnout.Geometry.NONE) {
516                if (isHeadOnPanel(continuingHead)
517                        && isHeadAssignedAnywhere(continuingHead)) {
518                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
519                            Bundle.getMessage("SignalsError8",
520                                    new Object[]{signalHeadName}),
521                            Bundle.getMessage("ErrorTitle"),
522                            JmriJOptionPane.ERROR_MESSAGE);
523                    return;
524                } else {
525                    removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
526                    removeAssignment(continuingHead);
527                    layoutTurnout.setSignalB1Name(signalHeadName);
528                }
529                //} else if (assigned != B1) {
530                //need to figure out what to do in this case - assigned to a different position on the same turnout.
531            }
532        }
533
534        signalHeadName = divergingSignalHeadComboBox.getSelectedItemDisplayName();
535        if (signalHeadName == null) {
536            signalHeadName = "";
537        }
538        if (setDiverging.isSelected()) {
539            if (isHeadOnPanel(divergingHead)
540                    && (divergingHead != getHeadFromName(layoutTurnout.getSignalC1Name()))) {
541                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
542                        Bundle.getMessage("SignalsError6",
543                                new Object[]{signalHeadName}),
544                        Bundle.getMessage("ErrorTitle"),
545                        JmriJOptionPane.ERROR_MESSAGE);
546                return;
547            } else {
548                removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
549                if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
550                    placeDiverging(signalHeadName);
551                } else {
552                    placeContinuing(signalHeadName);
553                }
554                removeAssignment(divergingHead);
555                layoutTurnout.setSignalC1Name(signalHeadName);
556                needRedraw = true;
557            }
558        } else {
559            LayoutTurnout.Geometry assigned = isHeadAssignedHere(divergingHead, layoutTurnout);
560            if (assigned == LayoutTurnout.Geometry.NONE) {
561                if (isHeadOnPanel(divergingHead)
562                        && isHeadAssignedAnywhere(divergingHead)) {
563                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
564                            Bundle.getMessage("SignalsError8",
565                                    new Object[]{signalHeadName}),
566                            Bundle.getMessage("ErrorTitle"),
567                            JmriJOptionPane.ERROR_MESSAGE);
568                    return;
569                } else {
570                    removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
571                    removeAssignment(divergingHead);
572                    layoutTurnout.setSignalC1Name(signalHeadName);
573                }
574                //} else if (assigned != C1) {
575                //need to figure out what to do in this case - assigned to a different position on the same turnout.
576            }
577        }
578        //setup Logic if requested and enough information is available
579        if (setupLogicThroatContinuing.isSelected()) {
580            setLogicThroatContinuing();
581        }
582        if ((throatDivergingHead != null) && setupLogicThroatDiverging.isSelected()) {
583            setLogicThroatDiverging();
584        }
585        if (setupLogicContinuing.isSelected()) {
586            setLogicContinuing();
587        }
588        if (setupLogicDiverging.isSelected()) {
589            setLogicDiverging();
590        }
591        //make sure this layout turnout is not linked to another
592        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
593        layoutTurnout.setLinkedTurnoutName("");
594        //finish up
595        setSignalsAtTurnoutOpenFlag = false;
596        setSignalsAtTurnoutFrame.setVisible(false);
597        if (needRedraw) {
598            layoutEditor.redrawPanel();
599            layoutEditor.setDirty();
600            needRedraw = false;
601        }
602    }   //setSignalsDonePressed
603
604    /**
605     * Checks the turnout name (could also be a crossover, ...)
606     * 1) checks setSignalsAtTurnoutFromMenuFlag (bis setSignalsAtXoverTurnoutFromMenuFlag)
607     *      skip to 6 if true
608     * 2) get a name string from turnoutComboBox (bis NamedBean.normalizeUserName(xoverTurnoutName) ),
609     *      showing an error dialog if not present
610     * 3) Gets turnout by that name, showing a error and returning false if not
611     * 4) (??) if the turnout's user name is non-existant or not matching, reset the turnout name string source
612     *              used in 2 (does this ever work?)
613     * 5) Search through all LayoutTurnout (and subclass) objects, looking for a match. If
614     *          is the other type (LayoutTurnout vs XOver or vice-versa), so an error,
615     *          call a cancel routine and return false. Save the found item in the
616     *          'layoutTurnout' non-local variable
617     *
618     * 6) If the above succeed in finding a layoutTurnout, calculate an angle and
619     *          store in the `placeSignalDirectionDEG` non-local variable
620     *          and return true (success)
621     * 7) Finally, show an error saying the turnout is not displayed on this panel and return false.
622     *
623     * In summary, this makes some checks, and then (re)loads the 'layoutTurnout' and
624     * 'placeSignalDirectionDEG' non-local variables, returning true for success
625     *
626     * @return true if ok, false if not for various reasons
627     */
628    private boolean getTurnoutInformation(boolean isCrossover) {
629        String str = "";
630        if (isCrossover ? !setSignalsAtXoverTurnoutFromMenuFlag : !setSignalsAtTurnoutFromMenuFlag) {
631            turnout = null;
632            layoutTurnout = null;
633            str = isCrossover ? NamedBean.normalizeUserName(xoverTurnoutName)
634                    : turnoutComboBox.getSelectedItemDisplayName();
635            if ((str == null) || str.isEmpty()) {
636                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
637                        Bundle.getMessage("SignalsError1"),
638                        Bundle.getMessage("ErrorTitle"),
639                        JmriJOptionPane.ERROR_MESSAGE);
640                return false;
641            }
642            turnout = InstanceManager.turnoutManagerInstance().getTurnout(str);
643            if (turnout == null) {
644                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
645                        Bundle.getMessage("SignalsError2",
646                                new Object[]{str}), Bundle.getMessage("ErrorTitle"),
647                        JmriJOptionPane.ERROR_MESSAGE);
648                return false;
649            } else {
650                String uname = turnout.getUserName();
651                if ((uname == null) || uname.isEmpty() || !uname.equals(str)) {
652                    if (isCrossover) {
653                        xoverTurnoutName = str;
654                    } else {
655                        turnoutComboBox.setSelectedItem(turnout);
656                    }
657                }
658            }
659            for (LayoutTurnout t : layoutEditor.getLayoutTurnouts()) {
660                if (t.getTurnout() == turnout) {
661                    layoutTurnout = t;
662                    if (t.isTurnoutTypeXover() != isCrossover) {
663                        if (isCrossover) {
664                            JmriJOptionPane.showMessageDialog(layoutEditor,
665                                    Bundle.getMessage("InfoMessage8"),
666                                    Bundle.getMessage("MessageTitle"),
667                                    JmriJOptionPane.INFORMATION_MESSAGE);
668                            setXoverSignalsCancelPressed(null);
669                        } else {
670                            JmriJOptionPane.showMessageDialog(layoutEditor,
671                                    Bundle.getMessage("InfoMessage1"),
672                                    Bundle.getMessage("MessageTitle"),
673                                    JmriJOptionPane.INFORMATION_MESSAGE);
674                            setSignalsCancelPressed(null);
675                        }
676                        return false;
677                    }
678                    break;
679                }
680            }
681        }
682
683        if (layoutTurnout != null) {
684            // convert to view to get angle on screen display
685            LayoutTurnoutView ltv = layoutEditor.getLayoutTurnoutView(layoutTurnout);
686
687            Point2D coordsA = ltv.getCoordsA(), coords2;
688            if (isCrossover) {
689                coords2 = ltv.getCoordsB();
690            } else {
691                coords2 = ltv.getCoordsCenter();
692            }
693            placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coords2, coordsA));
694            return true;
695        }
696        JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
697                Bundle.getMessage("SignalsError3",
698                        new Object[]{str}), Bundle.getMessage("ErrorTitle"),
699                JmriJOptionPane.ERROR_MESSAGE);
700        return false;
701    }   //getTurnoutInformation
702
703    private boolean getTurnoutSignalHeadInformation() {
704        throatContinuingHead = getSignalHeadFromEntry(throatContinuingSignalHeadComboBox, true, setSignalsAtTurnoutFrame);
705        if (throatContinuingHead == null) {
706            return false;
707        }
708        throatDivergingHead = getSignalHeadFromEntry(throatDivergingSignalHeadComboBox, false, setSignalsAtTurnoutFrame);
709        continuingHead = getSignalHeadFromEntry(continuingSignalHeadComboBox, true, setSignalsAtTurnoutFrame);
710        if (continuingHead == null) {
711            return false;
712        }
713        divergingHead = getSignalHeadFromEntry(divergingSignalHeadComboBox, true, setSignalsAtTurnoutFrame);
714        if (divergingHead == null) {
715            return false;
716        }
717        return true;
718    }
719    private NamedIcon testIcon = null;
720
721    private void placeThroatContinuing() {
722        if (testIcon == null) {
723            testIcon = signalIconEditor.getIcon(0);
724        }
725        String signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
726        if (signalHeadName == null) {
727            signalHeadName = "";
728        }
729        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
730
731        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
732
733        Point2D coordsA = layoutTurnoutView.getCoordsA();
734        Point2D delta = new Point2D.Double(+shift, +shift);
735
736        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
737        Point2D where = MathUtil.add(coordsA, delta);
738        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
739    }
740
741    private void placeThroatDiverging() {
742        if (testIcon == null) {
743            testIcon = signalIconEditor.getIcon(0);
744        }
745        String signalHeadName = throatDivergingSignalHeadComboBox.getSelectedItemDisplayName();
746        if (signalHeadName == null) {
747            signalHeadName = "";
748        }
749        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
750
751        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
752
753        Point2D coordsA = layoutTurnoutView.getCoordsA();
754        Point2D delta = new Point2D.Double(-shift, +shift);
755
756        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
757        Point2D where = MathUtil.add(coordsA, delta);
758        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
759    }
760
761    private void placeContinuing(@Nonnull String signalHeadName) {
762        if (testIcon == null) {
763            testIcon = signalIconEditor.getIcon(0);
764        }
765        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
766
767        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
768
769        Point2D coordsB = layoutTurnoutView.getCoordsB();
770        Point2D coordsC = layoutTurnoutView.getCoordsC();
771        Point2D coordsCenter = layoutTurnoutView.getCoordsCenter();
772
773        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
774        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
775        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
776        double shiftX = 0.0;
777        if (diffDirDEG < 0.0) {
778            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
779        }
780        Point2D delta = new Point2D.Double(shiftX, -shift);
781
782        delta = MathUtil.rotateDEG(delta, bDirDEG);
783        Point2D where = MathUtil.add(coordsB, delta);
784        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
785    }
786
787    private void placeDiverging(String signalHeadName) {
788        if (testIcon == null) {
789            testIcon = signalIconEditor.getIcon(0);
790        }
791        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
792
793        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
794
795        Point2D coordsB = layoutTurnoutView.getCoordsB();
796        Point2D coordsC = layoutTurnoutView.getCoordsC();
797        Point2D coordsCenter = layoutTurnoutView.getCoordsCenter();
798
799        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
800        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
801        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
802        double shiftX = 0.0;
803        if (diffDirDEG >= 0.0) {
804            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
805        }
806        Point2D delta = new Point2D.Double(shiftX, -shift);
807
808        delta = MathUtil.rotateDEG(delta, cDirDEG);
809        Point2D where = MathUtil.add(coordsC, delta);
810        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
811    }
812
813    private void setLogicThroatContinuing() {
814        TrackSegment track = null;
815        if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
816            track = (TrackSegment) layoutTurnout.getConnectB();
817        } else {
818            track = (TrackSegment) layoutTurnout.getConnectC();
819        }
820        if (track == null) {
821            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
822                    Bundle.getMessage("InfoMessage7"),
823                    Bundle.getMessage("MessageTitle"),
824                    JmriJOptionPane.INFORMATION_MESSAGE);
825            return;
826        }
827        LayoutBlock block = track.getLayoutBlock();
828        if (block == null) {
829            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
830                    Bundle.getMessage("InfoMessage6"),
831                    Bundle.getMessage("MessageTitle"),
832                    JmriJOptionPane.INFORMATION_MESSAGE);
833            return;
834        }
835        Sensor occupancy = block.getOccupancySensor();
836        if (occupancy == null) {
837            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
838                    Bundle.getMessage("InfoMessage4",
839                            new Object[]{block.getUserName()}),
840                    Bundle.getMessage("MessageTitle"),
841                    JmriJOptionPane.INFORMATION_MESSAGE);
842            return;
843        }
844
845        String signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
846        if (signalHeadName == null) {
847            signalHeadName = "";
848        }
849        SignalHead nextHead = getNextSignalFromObject(track,
850                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
851        if ((nextHead == null) && (!reachedEndBumper())) {
852            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
853                    Bundle.getMessage("InfoMessage5",
854                            new Object[]{block.getUserName()}),
855                    Bundle.getMessage("MessageTitle"),
856                    JmriJOptionPane.INFORMATION_MESSAGE);
857            return;
858        }
859        if (throatDivergingHead != null) {
860            if (!initializeBlockBossLogic(signalHeadName)) {
861                return;
862            }
863            logic.setMode(BlockBossLogic.TRAILINGMAIN);
864            logic.setTurnout(turnout.getDisplayName());
865            logic.setSensor1(occupancy.getDisplayName());
866            if (nextHead != null) {
867                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
868            }
869            if (auxSignal != null) {
870                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
871            }
872            finalizeBlockBossLogic();
873            return;
874        }
875        SignalHead savedAuxSignal = auxSignal;
876        TrackSegment track2 = null;
877        if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
878            track2 = (TrackSegment) layoutTurnout.getConnectC();
879        } else {
880            track2 = (TrackSegment) layoutTurnout.getConnectB();
881        }
882        if (track2 == null) {
883            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
884                    Bundle.getMessage("InfoMessage7"),
885                    Bundle.getMessage("MessageTitle"),
886                    JmriJOptionPane.INFORMATION_MESSAGE);
887            return;
888        }
889        LayoutBlock block2 = track2.getLayoutBlock();
890        if (block2 == null) {
891            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
892                    Bundle.getMessage("InfoMessage6"),
893                    Bundle.getMessage("MessageTitle"),
894                    JmriJOptionPane.INFORMATION_MESSAGE);
895            return;
896        }
897        Sensor occupancy2 = block2.getOccupancySensor();
898        if (occupancy2 == null) {
899            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
900                    Bundle.getMessage("InfoMessage4",
901                            new Object[]{block2.getUserName()}),
902                    Bundle.getMessage("MessageTitle"),
903                    JmriJOptionPane.INFORMATION_MESSAGE);
904            return;
905        }
906        signalHeadName = throatContinuingSignalHeadComboBox.getSelectedItemDisplayName();
907        if (signalHeadName == null) {
908            signalHeadName = "";
909        }
910        SignalHead nextHead2 = getNextSignalFromObject(track2,
911                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
912        if ((nextHead2 == null) && (!reachedEndBumper())) {
913            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
914                    Bundle.getMessage("InfoMessage5",
915                            new Object[]{block2.getUserName()}),
916                    Bundle.getMessage("MessageTitle"),
917                    JmriJOptionPane.INFORMATION_MESSAGE);
918            return;
919        }
920        if (!initializeBlockBossLogic(signalHeadName)) {
921            return;
922        }
923        logic.setMode(BlockBossLogic.FACING);
924        logic.setTurnout(turnout.getDisplayName());
925        logic.setWatchedSensor1(occupancy.getDisplayName());
926        logic.setWatchedSensor2(occupancy2.getDisplayName());
927        if (nextHead != null) {
928            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
929        }
930        if (savedAuxSignal != null) {
931            logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
932        }
933        if (nextHead2 != null) {
934            logic.setWatchedSignal2(nextHead2.getDisplayName());
935        }
936        if (auxSignal != null) {
937            logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
938        }
939        if (!layoutTurnout.isMainlineC()) {
940            logic.setLimitSpeed2(true);
941        }
942        finalizeBlockBossLogic();
943    }   //setLogicThroatContinuing
944
945    private void setLogicThroatDiverging() {
946        TrackSegment track = null;
947        if (layoutTurnout.getContinuingSense() == Turnout.CLOSED) {
948            track = (TrackSegment) layoutTurnout.getConnectC();
949        } else {
950            track = (TrackSegment) layoutTurnout.getConnectB();
951        }
952        if (track == null) {
953            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
954                    Bundle.getMessage("InfoMessage7"),
955                    Bundle.getMessage("MessageTitle"),
956                    JmriJOptionPane.INFORMATION_MESSAGE);
957            return;
958        }
959        LayoutBlock block = track.getLayoutBlock();
960        if (block == null) {
961            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
962                    Bundle.getMessage("InfoMessage6"),
963                    Bundle.getMessage("MessageTitle"),
964                    JmriJOptionPane.INFORMATION_MESSAGE);
965            return;
966        }
967        Sensor occupancy = block.getOccupancySensor();
968        if (occupancy == null) {
969            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
970                    Bundle.getMessage("InfoMessage4",
971                            new Object[]{block.getUserName()}),
972                    Bundle.getMessage("MessageTitle"),
973                    JmriJOptionPane.INFORMATION_MESSAGE);
974            return;
975        }
976        String signalHeadName = throatDivergingSignalHeadComboBox.getSelectedItemDisplayName();
977        if (signalHeadName == null) {
978            signalHeadName = "";
979        }
980        SignalHead nextHead = getNextSignalFromObject(track,
981                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
982        if ((nextHead == null) && (!reachedEndBumper())) {
983            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
984                    Bundle.getMessage("InfoMessage5",
985                            new Object[]{block.getUserName()}),
986                    Bundle.getMessage("MessageTitle"),
987                    JmriJOptionPane.INFORMATION_MESSAGE);
988            return;
989        }
990        if (!initializeBlockBossLogic(signalHeadName)) {
991            return;
992        }
993
994        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
995        logic.setTurnout(turnout.getDisplayName());
996        logic.setSensor1(occupancy.getDisplayName());
997        if (nextHead != null) {
998            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
999        }
1000        if (auxSignal != null) {
1001            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
1002        }
1003        if (!layoutTurnout.isMainlineC()) {
1004            logic.setLimitSpeed2(true);
1005        }
1006        finalizeBlockBossLogic();
1007    }   //setLogicThroatDiverging
1008
1009    private void setLogicContinuing() {
1010        TrackSegment track = (TrackSegment) layoutTurnout.getConnectA();
1011        if (track == null) {
1012            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1013                    Bundle.getMessage("InfoMessage7"),
1014                    Bundle.getMessage("MessageTitle"),
1015                    JmriJOptionPane.INFORMATION_MESSAGE);
1016            return;
1017        }
1018        LayoutBlock block = track.getLayoutBlock();
1019        if (block == null) {
1020            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1021                    Bundle.getMessage("InfoMessage6"),
1022                    Bundle.getMessage("MessageTitle"),
1023                    JmriJOptionPane.INFORMATION_MESSAGE);
1024            return;
1025        }
1026        Sensor occupancy = block.getOccupancySensor();
1027        if (occupancy == null) {
1028            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1029                    Bundle.getMessage("InfoMessage4",
1030                            new Object[]{block.getUserName()}),
1031                    Bundle.getMessage("MessageTitle"),
1032                    JmriJOptionPane.INFORMATION_MESSAGE);
1033            return;
1034        }
1035        String signalHeadName = continuingSignalHeadComboBox.getSelectedItemDisplayName();
1036        if (signalHeadName == null) {
1037            signalHeadName = "";
1038        }
1039        SignalHead nextHead = getNextSignalFromObject(track,
1040                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
1041        if ((nextHead == null) && (!reachedEndBumper())) {
1042            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1043                    Bundle.getMessage("InfoMessage5",
1044                            new Object[]{block.getUserName()}),
1045                    Bundle.getMessage("MessageTitle"),
1046                    JmriJOptionPane.INFORMATION_MESSAGE);
1047            return;
1048        }
1049        if (!initializeBlockBossLogic(signalHeadName)) {
1050            return;
1051        }
1052        logic.setMode(BlockBossLogic.TRAILINGMAIN);
1053        logic.setTurnout(turnout.getDisplayName());
1054        logic.setSensor1(occupancy.getDisplayName());
1055        if (nextHead != null) {
1056            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
1057        }
1058        if (auxSignal != null) {
1059            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
1060        }
1061        finalizeBlockBossLogic();
1062    }   //setLogicContinuing
1063
1064    private void setLogicDiverging() {
1065        TrackSegment track = (TrackSegment) layoutTurnout.getConnectA();
1066        if (track == null) {
1067            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1068                    Bundle.getMessage("InfoMessage7"),
1069                    Bundle.getMessage("MessageTitle"),
1070                    JmriJOptionPane.INFORMATION_MESSAGE);
1071            return;
1072        }
1073        LayoutBlock block = track.getLayoutBlock();
1074        if (block == null) {
1075            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1076                    Bundle.getMessage("InfoMessage6"),
1077                    Bundle.getMessage("MessageTitle"),
1078                    JmriJOptionPane.INFORMATION_MESSAGE);
1079            return;
1080        }
1081        Sensor occupancy = block.getOccupancySensor();
1082        if (occupancy == null) {
1083            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1084                    Bundle.getMessage("InfoMessage4",
1085                            new Object[]{block.getUserName()}),
1086                    Bundle.getMessage("MessageTitle"),
1087                    JmriJOptionPane.INFORMATION_MESSAGE);
1088            return;
1089        }
1090        String signalHeadName = divergingSignalHeadComboBox.getSelectedItemDisplayName();
1091        if (signalHeadName == null) {
1092            signalHeadName = "";
1093        }
1094        SignalHead nextHead = getNextSignalFromObject(track,
1095                layoutTurnout, signalHeadName, setSignalsAtTurnoutFrame);
1096        if ((nextHead == null) && (!reachedEndBumper())) {
1097            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
1098                    Bundle.getMessage("InfoMessage5",
1099                            new Object[]{block.getUserName()}),
1100                    Bundle.getMessage("MessageTitle"),
1101                    JmriJOptionPane.INFORMATION_MESSAGE);
1102            return;
1103        }
1104        if (!initializeBlockBossLogic(signalHeadName)) {
1105            return;
1106        }
1107        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
1108        logic.setTurnout(turnout.getDisplayName());
1109        logic.setSensor1(occupancy.getDisplayName());
1110        if (nextHead != null) {
1111            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
1112        }
1113        if (auxSignal != null) {
1114            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
1115        }
1116        if (!layoutTurnout.isMainlineC()) {
1117            logic.setLimitSpeed2(true);
1118        }
1119        finalizeBlockBossLogic();
1120    }   //setLogicDiverging
1121
1122    /*==========================================*\
1123    | * Utility routines used by multiple tools *|
1124    \*==========================================*/
1125    /**
1126     * Returns the layout turnout corresponding to a given turnout.
1127     * <p>
1128     * If require
1129     * double crossover is requested, an error message is sent to the user if
1130     * the layout turnout is not a double crossover, and null is returned.
1131     * <p>
1132     * If a layout turnout corresponding to the turnout is not found, an error
1133     * message is sent to the user and null is returned.
1134     *
1135     * @param turnout the base turnout.
1136     * @param requireDoubleXover true to force checking of turnout type.
1137     * @param str error message string.
1138     * @param theFrame main frame.
1139     * @return layout turnout, may be null.
1140     */
1141    @CheckReturnValue
1142    public LayoutTurnout getLayoutTurnoutFromTurnout(
1143            @Nonnull Turnout turnout,
1144            boolean requireDoubleXover,
1145            @Nonnull String str,
1146            @CheckForNull JFrame theFrame) {
1147        for (LayoutTurnout t : layoutEditor.getLayoutTurnouts()) {
1148            if (t.getTurnout() == turnout) {
1149                //have the layout turnout corresponding to the turnout
1150                if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
1151                        && (!requireDoubleXover)) {
1152                    JmriJOptionPane.showMessageDialog(theFrame,
1153                            Bundle.getMessage("InfoMessage1"),
1154                            Bundle.getMessage("MessageTitle"),
1155                            JmriJOptionPane.INFORMATION_MESSAGE);
1156                    return null;
1157                }
1158                if (requireDoubleXover && (t.getTurnoutType() != LayoutTurnout.TurnoutType.DOUBLE_XOVER)) {
1159                    JmriJOptionPane.showMessageDialog(theFrame,
1160                            Bundle.getMessage("InfoMessage8"),
1161                            Bundle.getMessage("MessageTitle"),
1162                            JmriJOptionPane.INFORMATION_MESSAGE);
1163                    return null;
1164                }
1165                return t;
1166            }
1167        }
1168        //layout turnout not found
1169        JmriJOptionPane.showMessageDialog(theFrame,
1170                Bundle.getMessage("SignalsError3",
1171                        new Object[]{str}), Bundle.getMessage("ErrorTitle"),
1172                JmriJOptionPane.ERROR_MESSAGE);
1173        return null;
1174    }
1175
1176    /**
1177     * Returns the SignalHead corresponding to an entry field in the specified
1178     * dialog. This also takes care of UpperCase and trimming of leading and
1179     * trailing blanks. If entry is required, and no entry is present, and error
1180     * message is sent. An error message also results if a signal head with the
1181     * entered name is not found in the SignalTable.
1182     * @param signalNameComboBox the combo box with signal name selected.
1183     * @param requireEntry true if mandatory field, else false.
1184     * @param frame the main frame.
1185     * @return signal head, may be null.
1186     */
1187    @CheckReturnValue
1188    public SignalHead getSignalHeadFromEntry(
1189            @Nonnull NamedBeanComboBox<SignalHead> signalNameComboBox,
1190            boolean requireEntry,
1191            @Nonnull JmriJFrame frame) {
1192        String signalName = signalNameComboBox.getSelectedItemDisplayName();
1193        SignalHead result = getSignalHeadFromEntry(signalName, requireEntry, frame);
1194        if (result != null) {
1195            String uname = result.getUserName();
1196            if ((uname == null) || uname.isEmpty() || !uname.equals(signalName)) {
1197                signalNameComboBox.setSelectedItem(result);
1198            }
1199        }
1200        return result;
1201    }
1202
1203    @CheckReturnValue
1204    public SignalHead getSignalHeadFromEntry(
1205            @Nonnull JTextField signalNameTextField,
1206            boolean requireEntry, @Nonnull JmriJFrame frame) {
1207        String signalName = NamedBean.normalizeUserName(signalNameTextField.getText());
1208        SignalHead result = getSignalHeadFromEntry(signalName, requireEntry, frame);
1209        if (result != null) {
1210            String uname = result.getUserName();
1211            if ((uname == null) || uname.isEmpty() || !uname.equals(signalName)) {
1212                signalNameTextField.setText(signalName);
1213            }
1214        }
1215        return result;
1216    }
1217
1218    @CheckReturnValue
1219    public SignalHead getSignalHeadFromEntry(@CheckForNull String signalName,
1220            boolean requireEntry, @Nonnull JmriJFrame frame) {
1221        if ((signalName == null) || signalName.isEmpty()) {
1222            if (requireEntry) {
1223                JmriJOptionPane.showMessageDialog(frame, Bundle.getMessage("SignalsError5"),
1224                        Bundle.getMessage("ErrorTitle"),
1225                        JmriJOptionPane.ERROR_MESSAGE);
1226            }
1227            return null;
1228        }
1229        SignalHead head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1230        if (head == null) {
1231            JmriJOptionPane.showMessageDialog(frame,
1232                    Bundle.getMessage("SignalsError4",
1233                            new Object[]{signalName}), Bundle.getMessage("ErrorTitle"),
1234                    JmriJOptionPane.ERROR_MESSAGE);
1235            return null;
1236        }
1237        return (head);
1238    }
1239
1240    /**
1241     * Returns a SignalHead given a name.
1242     * @param str signal head name.
1243     * @return signal head, may be null.
1244     */
1245    @CheckReturnValue
1246    public SignalHead getHeadFromName(@CheckForNull String str) {
1247        SignalHead result = null;
1248        if ((str != null) && !str.isEmpty()) {
1249            result = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(str);
1250        }
1251        return result;
1252    }
1253
1254    /**
1255     * Places a signal head icon on the panel after rotation at the designated
1256     * place, with all icons taken care of.
1257     *
1258     * @param directionDEG   rotation in degrees.
1259     * @param signalHeadName name of a signal head.
1260     * @param where          coordinates for placing signal head on panel.
1261     */
1262    public void setSignalHeadOnPanel(double directionDEG,
1263            @Nonnull String signalHeadName,
1264            @Nonnull Point2D where) {
1265        setSignalHeadOnPanel(directionDEG, signalHeadName, (int) where.getX(), (int) where.getY());
1266    }
1267
1268    /**
1269     * Places a signal head icon on the panel after rotation at the designated
1270     * place, with all icons taken care of.
1271     *
1272     * @param directionDEG   rotation in degrees.
1273     * @param signalHeadName name of a signal head.
1274     * @param xLoc           x coordinate for placing signal head on panel.
1275     * @param yLoc           y coordinate for placing signal head on panel.
1276     */
1277    public void setSignalHeadOnPanel(double directionDEG, @Nonnull String signalHeadName, int xLoc, int yLoc) {
1278        SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
1279
1280        if (directionDEG > 0) {
1281            Iterator<String> e = l.getIconStateNames();
1282            while (e.hasNext()) {
1283                l.getIcon(e.next()).rotate((int) directionDEG, l);
1284            }
1285        }
1286
1287        l.setLocation(xLoc - (int) (l.maxWidth() / 2.0), yLoc - (int) (l.maxHeight() / 2.0));
1288
1289        layoutEditor.putSignal(l);
1290    }
1291
1292    /**
1293     * Returns an index if the specified signal head is assigned to the
1294     * LayoutTurnout initialized. Otherwise returns the NONE index. The index
1295     * specifies the turnout position of the signal head according to the code
1296     * listed at the beginning of this module.
1297     */
1298    private LayoutTurnout.Geometry isHeadAssignedHere(@Nonnull SignalHead head, @Nonnull LayoutTurnout lTurnout) {
1299        LayoutTurnout.Geometry result = LayoutTurnout.Geometry.NONE;
1300
1301        Map<String, LayoutTurnout.Geometry> map = new HashMap<>();
1302        map.put(lTurnout.getSignalA1Name(), LayoutTurnout.Geometry.POINTA1);
1303        map.put(lTurnout.getSignalA2Name(), LayoutTurnout.Geometry.POINTA2);
1304        map.put(lTurnout.getSignalA3Name(), LayoutTurnout.Geometry.POINTA3);
1305        map.put(lTurnout.getSignalB1Name(), LayoutTurnout.Geometry.POINTB1);
1306        map.put(lTurnout.getSignalB2Name(), LayoutTurnout.Geometry.POINTB2);
1307        map.put(lTurnout.getSignalC1Name(), LayoutTurnout.Geometry.POINTC1);
1308        map.put(lTurnout.getSignalC2Name(), LayoutTurnout.Geometry.POINTC2);
1309        map.put(lTurnout.getSignalD1Name(), LayoutTurnout.Geometry.POINTD1);
1310        map.put(lTurnout.getSignalD2Name(), LayoutTurnout.Geometry.POINTD2);
1311
1312        String sName = head.getSystemName();
1313        String uName = head.getUserName();
1314
1315        for (Map.Entry<String, LayoutTurnout.Geometry> entry : map.entrySet()) {
1316            String signalName = entry.getKey();
1317
1318            if (!signalName.isEmpty() && (signalName.equals(sName) || signalName.equals(uName))) {
1319                result = entry.getValue();
1320                break;
1321            }
1322        }
1323
1324        return result;
1325    }
1326
1327    /**
1328     * Get if signal head is on the panel.
1329     * @param head the signal head to locate.
1330     * @return true if an icon for the specified SignalHead is on the panel.
1331     */
1332    public boolean isHeadOnPanel(@Nonnull SignalHead head) {
1333        for (SignalHeadIcon h : layoutEditor.getSignalList()) {
1334            if (h.getSignalHead() == head) {
1335                return true;
1336            }
1337        }
1338        return false;
1339    }
1340
1341    /**
1342     * Returns true if the specified Signal Head is assigned to an object on the
1343     * panel, regardless of whether an icon is displayed or not.
1344     * @param head the signal head to locate.
1345     * @return true if the signal head is attached to a panel object.
1346     */
1347    public boolean isHeadAssignedAnywhere(@Nonnull SignalHead head) {
1348        String sName = head.getSystemName();
1349        String uName = head.getUserName();
1350
1351        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
1352            if (isHeadAssignedHere(head, to) != LayoutTurnout.Geometry.NONE) {
1353                return true;
1354            }
1355        }
1356
1357        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
1358            if ((po.getEastBoundSignal().equals(sName) || ((uName != null)
1359                    && (po.getEastBoundSignal().equals(uName))))) {
1360                return true;
1361            }
1362            if ((po.getWestBoundSignal().equals(sName) || ((uName != null)
1363                    && (po.getWestBoundSignal().equals(uName))))) {
1364                return true;
1365            }
1366        }
1367
1368        for (LevelXing x : layoutEditor.getLevelXings()) {
1369            if (  (x.getSignalAName().equals(sName) || ((uName != null)
1370                    && (x.getSignalAName().equals(uName))))) {
1371                return true;
1372            }
1373            if (  (x.getSignalBName().equals(sName) || ((uName != null)
1374                    && (x.getSignalBName().equals(uName))))) {
1375                return true;
1376            }
1377            if (  (x.getSignalCName().equals(sName) || ((uName != null)
1378                    && (x.getSignalCName().equals(uName))))) {
1379                return true;
1380            }
1381            if (  (x.getSignalDName().equals(sName) || ((uName != null)
1382                    && (x.getSignalDName().equals(uName))))) {
1383                return true;
1384            }
1385        }
1386        return false;
1387    }   //isHeadAssignedAnywhere
1388
1389    /**
1390     * Removes the assignment of the specified SignalHead to either a turnout, a
1391     * positionable point, or a level crossing wherever it is assigned.
1392     * @param head the signal head to be removed.
1393     */
1394    public void removeAssignment(@Nonnull SignalHead head) {
1395        String sName = head.getSystemName();
1396        String uName = head.getUserName();
1397        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
1398            if ((to.getSignalA1Name().equals(sName) || ((uName != null)
1399                    && to.getSignalA1Name().equals(uName)))) {
1400                to.setSignalA1Name("");
1401            }
1402            if ((to.getSignalA2Name().equals(sName) || ((uName != null)
1403                    && to.getSignalA2Name().equals(uName)))) {
1404                to.setSignalA2Name("");
1405            }
1406            if ((to.getSignalA3Name().equals(sName) || ((uName != null)
1407                    && to.getSignalA3Name().equals(uName)))) {
1408                to.setSignalA3Name("");
1409            }
1410            if ((to.getSignalB1Name().equals(sName) || ((uName != null)
1411                    && to.getSignalB1Name().equals(uName)))) {
1412                to.setSignalB1Name("");
1413            }
1414            if ((to.getSignalB2Name().equals(sName) || ((uName != null)
1415                    && to.getSignalB2Name().equals(uName)))) {
1416                to.setSignalB2Name("");
1417            }
1418            if ((to.getSignalC1Name().equals(sName) || ((uName != null)
1419                    && to.getSignalC1Name().equals(uName)))) {
1420                to.setSignalC1Name("");
1421            }
1422            if ((to.getSignalC2Name().equals(sName) || ((uName != null)
1423                    && to.getSignalC2Name().equals(uName)))) {
1424                to.setSignalC2Name("");
1425            }
1426            if ((to.getSignalD1Name().equals(sName) || ((uName != null)
1427                    && to.getSignalD1Name().equals(uName)))) {
1428                to.setSignalD1Name("");
1429            }
1430            if ((to.getSignalD2Name().equals(sName) || ((uName != null)
1431                    && to.getSignalD2Name().equals(uName)))) {
1432                to.setSignalD2Name("");
1433            }
1434        }
1435        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
1436            if (po.getEastBoundSignal().equals(sName) || po.getEastBoundSignal().equals(uName)) {
1437                po.setEastBoundSignal("");
1438            }
1439            if (po.getWestBoundSignal().equals(sName) || po.getWestBoundSignal().equals(uName)) {
1440                po.setWestBoundSignal("");
1441            }
1442        }
1443        for (LevelXing x : layoutEditor.getLevelXings()) {
1444            if ( (x.getSignalAName().equals(sName) || ((uName != null)
1445                    && (x.getSignalAName().equals(uName))))) {
1446                x.setSignalAName("");
1447            }
1448            if ( (x.getSignalBName().equals(sName) || ((uName != null)
1449                    && (x.getSignalBName().equals(uName))))) {
1450                x.setSignalBName("");
1451            }
1452            if ( (x.getSignalCName().equals(sName) || ((uName != null)
1453                    && (x.getSignalCName().equals(uName))))) {
1454                x.setSignalCName("");
1455            }
1456            if ( (x.getSignalDName().equals(sName) || ((uName != null)
1457                    && (x.getSignalDName().equals(uName))))) {
1458                x.setSignalDName("");
1459            }
1460        }
1461    }   //removeAssignment
1462
1463    /**
1464     * Removes the SignalHead with the specified name from the panel and from
1465     * assignment to any turnout, positionable point, or level crossing.
1466     * @param signalName name of signal head to be removed.
1467     */
1468    public void removeSignalHeadFromPanel(@CheckForNull String signalName) {
1469        if ((signalName == null) || signalName.isEmpty()) {
1470            return;
1471        }
1472        SignalHead head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1473        if (head != null) {
1474            removeAssignment(head);
1475            layoutEditor.removeSignalHead(head);
1476        }
1477    }
1478
1479    /*
1480    * Initializes a BlockBossLogic for creation of a signal logic for the signal
1481    * head named in "signalHeadName".
1482    * Should not be called until enough informmation has been gathered to allow
1483    * configuration of the Simple Signal Logic.
1484     */
1485    public boolean initializeBlockBossLogic(@Nonnull String signalHeadName) {
1486        logic = BlockBossLogic.getStoppedObject(signalHeadName);
1487        return true;
1488    }
1489
1490    /*
1491    * Finalizes a successfully created signal logic
1492     */
1493    public void finalizeBlockBossLogic() {
1494        if (logic == null) {
1495            return;
1496        }
1497        InstanceManager.getDefault(BlockBossLogicProvider.class).register(logic);
1498        logic.start();
1499        logic = null;
1500    }
1501
1502    /*
1503     * Returns the signal head at the end of the block "track" is assigned to.
1504     *  "track" is the Track Segment leaving "object".
1505     *  "object" must be either an anchor point or one of the connecting
1506     *  points of a turnout or level crossing.
1507     * Note: returns 'null' is signal is not present where it is expected, or
1508     *  if an End Bumper is reached. To test for end bumper, use the
1509     *  associated routine "reachedEndBumper()". Reaching a turntable ray
1510     *  track connection is considered reaching an end bumper.
1511     * Note: Normally this routine requires a signal at any turnout it finds.
1512     *  However, if 'skipIncludedTurnout' is true, this routine will skip
1513     *  over an absent signal at an included turnout, that is a turnout
1514     *  with its throat track segment and its continuing track segment in
1515     *  the same block. When this happens, the user is warned.
1516     */
1517    @CheckReturnValue
1518    public SignalHead getNextSignalFromObject(@Nonnull TrackSegment track,
1519            @Nonnull Object object,
1520            @Nonnull String signalHeadName, @Nonnull JmriJFrame frame) {
1521        hitEndBumper = false;
1522        auxSignal = null;
1523        TrackSegment t = track;
1524        Object obj = object;
1525        boolean inBlock = true;
1526        HitPointType type; // = LayoutEditor.HitPointType.NONE;
1527        Object connect = null;
1528        while (inBlock) {
1529            if (t.getConnect1() == obj) {
1530                type = t.getType2();
1531                connect = t.getConnect2();
1532            } else if (t.getConnect2() == obj) {
1533                type = t.getType1();
1534                connect = t.getConnect1();
1535            } else {
1536                log.error("Error obj not connected to {}!", t.getName());
1537                return null;
1538            }
1539            if (type == HitPointType.POS_POINT) {
1540                PositionablePoint p = (PositionablePoint) connect;
1541                if (p.getType() == PositionablePoint.PointType.END_BUMPER) {
1542                    hitEndBumper = true;
1543                    return null;
1544                }
1545                if (p.getConnect1() == t) {
1546                    t = p.getConnect2();
1547                } else {
1548                    t = p.getConnect1();
1549                }
1550                if (t == null) {
1551                    return null;
1552                }
1553                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1554                    //p is a block boundary - should be signalled
1555                    String signalName;
1556                    if (isAtWestEndOfAnchor(t, p)) {
1557                        signalName = p.getWestBoundSignal();
1558                    } else {
1559                        signalName = p.getEastBoundSignal();
1560                    }
1561                    if (signalName.isEmpty()) {
1562                        return null;
1563                    }
1564                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1565                }
1566                obj = p;
1567            } else if (type == HitPointType.TURNOUT_A) {
1568                //Reached turnout throat, should be signalled
1569                LayoutTurnout to = (LayoutTurnout) connect;
1570                String signalName = to.getSignalA2Name();
1571                if (!signalName.isEmpty()) {
1572                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1573                }
1574                signalName = to.getSignalA1Name();
1575                if (signalName.isEmpty()) {
1576                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1577                        return null;
1578                    }
1579                    t = getContinuingTrack(to, type);
1580                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1581                        return null;
1582                    }
1583                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1584                    obj = to;
1585                } else {
1586                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1587                }
1588            } else if (type == HitPointType.TURNOUT_B) {
1589                //Reached turnout continuing, should be signalled
1590                LayoutTurnout to = (LayoutTurnout) connect;
1591                String signalName = to.getSignalB2Name();
1592                if (to.getContinuingSense() == Turnout.THROWN) {
1593                    signalName = to.getSignalC2Name();
1594                }
1595                if (!signalName.isEmpty()) {
1596                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1597                }
1598                if (to.getContinuingSense() == Turnout.CLOSED) {
1599                    signalName = to.getSignalB1Name();
1600                } else {
1601                    signalName = to.getSignalC1Name();
1602                }
1603                if (signalName.isEmpty()) {
1604                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1605                        return null;
1606                    }
1607                    t = getContinuingTrack(to, type);
1608                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1609                        return null;
1610                    }
1611                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1612                    obj = to;
1613                } else {
1614                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1615                }
1616            } else if (type == HitPointType.TURNOUT_C) {
1617                //Reached turnout diverging, should be signalled
1618                LayoutTurnout to = (LayoutTurnout) connect;
1619                String signalName = to.getSignalC2Name();
1620                if (to.getContinuingSense() == Turnout.THROWN) {
1621                    signalName = to.getSignalB2Name();
1622                }
1623                if (!signalName.isEmpty()) {
1624                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1625                }
1626                if (to.getContinuingSense() == Turnout.CLOSED) {
1627                    signalName = to.getSignalC1Name();
1628                } else {
1629                    signalName = to.getSignalB1Name();
1630                }
1631                if (signalName.isEmpty()) {
1632                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1633                        return null;
1634                    }
1635                    t = getContinuingTrack(to, type);
1636                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1637                        return null;
1638                    }
1639                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1640                    obj = to;
1641                } else {
1642                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1643                }
1644            } else if (type == HitPointType.TURNOUT_D) {
1645                //Reached turnout xover 4, should be signalled
1646                LayoutTurnout to = (LayoutTurnout) connect;
1647                String signalName = to.getSignalD2Name();
1648                if (!signalName.isEmpty()) {
1649                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1650                }
1651                signalName = to.getSignalD1Name();
1652                if (signalName.isEmpty()) {
1653                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1654                        return null;
1655                    }
1656                    t = getContinuingTrack(to, type);
1657                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1658                        return null;
1659                    }
1660                    warnOfSkippedTurnout(frame, to.getTurnoutName(), signalHeadName);
1661                    obj = to;
1662                } else {
1663                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1664                }
1665            } else if (type == HitPointType.LEVEL_XING_A) {
1666                //Reached level crossing that may or may not be a block boundary
1667                LevelXing x = (LevelXing) connect;
1668                String signalName = x.getSignalAName();
1669                if ( !signalName.isEmpty() ) {
1670                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1671                }
1672                t = (TrackSegment) x.getConnectC();
1673                if (t == null) {
1674                    return null;
1675                }
1676                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1677                    return null;
1678                }
1679                obj = x;
1680            } else if (type == HitPointType.LEVEL_XING_B) {
1681                //Reached level crossing that may or may not be a block boundary
1682                LevelXing x = (LevelXing) connect;
1683                String signalName = x.getSignalBName();
1684                if ( !signalName.isEmpty() ) {
1685                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1686                }
1687                t = (TrackSegment) x.getConnectD();
1688                if (t == null) {
1689                    return null;
1690                }
1691                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1692                    return null;
1693                }
1694                obj = x;
1695            } else if (type == HitPointType.LEVEL_XING_C) {
1696                //Reached level crossing that may or may not be a block boundary
1697                LevelXing x = (LevelXing) connect;
1698                String signalName = x.getSignalCName();
1699                if ( !signalName.isEmpty() ) {
1700                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1701                }
1702                t = (TrackSegment) x.getConnectA();
1703                if (t == null) {
1704                    return null;
1705                }
1706                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1707                    return null;
1708                }
1709                obj = x;
1710            } else if (type == HitPointType.LEVEL_XING_D) {
1711                //Reached level crossing that may or may not be a block boundary
1712                LevelXing x = (LevelXing) connect;
1713                String signalName = x.getSignalDName();
1714                if ( !signalName.isEmpty() ) {
1715                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1716                }
1717                t = (TrackSegment) x.getConnectB();
1718                if (t == null) {
1719                    return null;
1720                }
1721                if (track.getLayoutBlock() != t.getLayoutBlock()) {
1722                    return null;
1723                }
1724                obj = x;
1725            } else if (type == HitPointType.SLIP_A) {
1726                LayoutSlip sl = (LayoutSlip) connect;
1727                String signalName = sl.getSignalA2Name();
1728                if (!signalName.isEmpty()) {
1729                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1730                }
1731                signalName = sl.getSignalA1Name();
1732                if (signalName.isEmpty()) {
1733                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1734                        return null;
1735                    }
1736                    t = getContinuingTrack(sl, type);
1737                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1738                        return null;
1739                    }
1740                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1741                    obj = sl;
1742                } else {
1743                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1744                }
1745            } else if (type == HitPointType.SLIP_B) {
1746                LayoutSlip sl = (LayoutSlip) connect;
1747                String signalName;
1748                if (sl.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
1749                    signalName = sl.getSignalB2Name();
1750                    if (!signalName.isEmpty()) {
1751                        auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1752                    }
1753                }
1754                signalName = sl.getSignalB1Name();
1755                if (signalName.isEmpty()) {
1756                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1757                        return null;
1758                    }
1759                    t = getContinuingTrack(sl, type);
1760                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1761                        return null;
1762                    }
1763                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1764                    obj = sl;
1765                } else {
1766                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1767                }
1768            } else if (type == HitPointType.SLIP_C) {
1769                LayoutSlip sl = (LayoutSlip) connect;
1770                String signalName;
1771                if (sl.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
1772                    signalName = sl.getSignalC2Name();
1773                    if (!signalName.isEmpty()) {
1774                        auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1775                    }
1776                }
1777                signalName = sl.getSignalC1Name();
1778                if (signalName.isEmpty()) {
1779                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1780                        return null;
1781                    }
1782                    t = getContinuingTrack(sl, type);
1783                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1784                        return null;
1785                    }
1786                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1787                    obj = sl;
1788                } else {
1789                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1790                }
1791            } else if (type == HitPointType.SLIP_D) {
1792                LayoutSlip sl = (LayoutSlip) connect;
1793                String signalName = sl.getSignalD2Name();
1794                if (!signalName.isEmpty()) {
1795                    auxSignal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1796                }
1797                signalName = sl.getSignalD1Name();
1798                if (signalName.isEmpty()) {
1799                    if (!layoutEditor.isIncludedTurnoutSkipped()) {
1800                        return null;
1801                    }
1802                    t = getContinuingTrack(sl, type);
1803                    if ((t == null) || (track.getLayoutBlock() != t.getLayoutBlock())) {
1804                        return null;
1805                    }
1806                    warnOfSkippedTurnout(frame, sl.getTurnoutName(), signalHeadName);
1807                    obj = sl;
1808                } else {
1809                    return InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(signalName);
1810                }
1811            } else if (HitPointType.isTurntableRayHitType(type)) {
1812                hitEndBumper = true;
1813                return null;
1814            }
1815        }
1816        return null;
1817    }   //getNextSignalFromObject
1818
1819    private boolean hitEndBumper = false;
1820
1821    private void warnOfSkippedTurnout(
1822            @Nonnull JFrame frame,
1823            @Nonnull String turnoutName,
1824            @Nonnull String signalHeadName) {
1825        JmriJOptionPane.showMessageDialog(frame,
1826                Bundle.getMessage("SignalsWarn2",
1827                        new Object[]{turnoutName, signalHeadName}),
1828                Bundle.getMessage("WarningTitle"),
1829                JmriJOptionPane.WARNING_MESSAGE);
1830    }
1831
1832    @CheckReturnValue
1833    private TrackSegment getContinuingTrack(@Nonnull LayoutTurnout to, HitPointType type) {
1834        LayoutTurnout.TurnoutType ty = to.getTurnoutType();
1835        if ((ty == LayoutTurnout.TurnoutType.RH_TURNOUT) || (ty == LayoutTurnout.TurnoutType.LH_TURNOUT)) {
1836            if (type == HitPointType.TURNOUT_A) {
1837                if (to.getContinuingSense() == Turnout.CLOSED) {
1838                    return (TrackSegment) to.getConnectB();
1839                } else {
1840                    return (TrackSegment) to.getConnectC();
1841                }
1842            } else {
1843                return (TrackSegment) to.getConnectA();
1844            }
1845        } else if ((ty == LayoutTurnout.TurnoutType.DOUBLE_XOVER) || (ty == LayoutTurnout.TurnoutType.RH_XOVER)
1846                || (ty == LayoutTurnout.TurnoutType.LH_XOVER)) {
1847            if (type == HitPointType.TURNOUT_A) {
1848                return (TrackSegment) to.getConnectB();
1849            } else if (type == HitPointType.TURNOUT_B) {
1850                return (TrackSegment) to.getConnectA();
1851            } else if (type == HitPointType.TURNOUT_C) {
1852                return (TrackSegment) to.getConnectD();
1853            } else if (type == HitPointType.TURNOUT_D) {
1854                return (TrackSegment) to.getConnectC();
1855            }
1856        }
1857        log.error("Bad connection type around turnout {}", to.getTurnoutName());
1858        return null;
1859    }
1860
1861    /*
1862     * Returns 'true' if an end bumper was reached during the last call to
1863     *  GetNextSignalFromObject. Also used in the odd case of reaching a
1864     *  turntable ray track connection, which is treated as an end
1865     *  bumper here.
1866     */
1867    public boolean reachedEndBumper() {
1868        return hitEndBumper;
1869    }
1870
1871    /*
1872     * Returns 'true' if "track" enters a block boundary at the west(north) end of
1873     *  "point". Returns "false" otherwise. If track is neither horizontal or
1874     *  vertical, assumes horizontal, as done when setting signals at block boundary.
1875     *  "track" is a TrackSegment connected to "point".
1876     *  "point" is an anchor point serving as a block boundary.
1877     * <p>
1878     * This is the member function method, which is preferred. See the static
1879     * method following.
1880     */
1881    public boolean isAtWestEndOfAnchor(TrackSegment t, PositionablePoint p) {
1882        return LayoutEditorTools.isAtWestEndOfAnchor(layoutEditor, t, p); // invoke status locally
1883    }
1884
1885
1886    /*
1887     * Returns 'true' if "track" enters a block boundary at the west(north) end of
1888     *  "point". Returns "false" otherwise. If track is neither horizontal or
1889     *  vertical, assumes horizontal, as done when setting signals at block boundary.
1890     *  "track" is a TrackSegment connected to "point".
1891     *  "point" is an anchor point serving as a block boundary.
1892     * <p>
1893     * This is the static form, which requires context information from
1894     * a passed LayoutEditor reference; the member function is preferred because
1895     * some day we want to get rid of the LayoutEditor combined pseudo-global and panel reference.
1896     */
1897    public static boolean isAtWestEndOfAnchor(LayoutEditor layoutEditor, TrackSegment t, PositionablePoint p) {
1898        if (p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
1899            if (p.getConnect1() == t) {
1900                if (p.getConnect1Dir() == Path.NORTH || p.getConnect1Dir() == Path.WEST) {
1901                    return false;
1902                }
1903                return true;
1904            } else {
1905                if (p.getConnect1Dir() == Path.NORTH || p.getConnect1Dir() == Path.WEST) {
1906                    return true;
1907                }
1908                return false;
1909            }
1910
1911        }
1912        TrackSegment tx = null;
1913        if (p.getConnect1() == t) {
1914            tx = p.getConnect2();
1915        } else if (p.getConnect2() == t) {
1916            tx = p.getConnect1();
1917        } else {
1918            log.error("track not connected to anchor point");
1919            return false;
1920        }
1921
1922        Point2D coords1;
1923        if (t.getConnect1() == p) {
1924            coords1 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
1925        } else {
1926            coords1 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
1927        }
1928
1929        Point2D coords2;
1930        if (tx != null) {
1931            if (tx.getConnect1() == p) {
1932                coords2 = layoutEditor.getCoords(tx.getConnect2(), tx.getType2());
1933            } else {
1934                coords2 = layoutEditor.getCoords(tx.getConnect1(), tx.getType1());
1935            }
1936        } else {
1937            if (t.getConnect1() == p) {
1938                coords2 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
1939            } else {
1940                coords2 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
1941            }
1942        }
1943
1944        double delX = coords1.getX() - coords2.getX();
1945        double delY = coords1.getY() - coords2.getY();
1946        if (Math.abs(delX) > 2.0 * Math.abs(delY)) {
1947            //track is primarily horizontal
1948            if (delX > 0.0) {
1949                return false;
1950            } else {
1951                return true;
1952            }
1953        } else if (Math.abs(delY) > 2.0 * Math.abs(delX)) {
1954            //track is primarily vertical
1955            if (delY > 0.0) {
1956                return false;
1957            } else {
1958                return true;
1959            }
1960        }
1961        // track is not primarily horizontal or vertical; assume horizontal
1962        // log.error ("Track is not vertical or horizontal at anchor");
1963        if (delX > 0.0) {
1964            return false;
1965        }
1966        return true;
1967    }
1968
1969    /*===========================*\
1970    |* setSignalsAtBlockBoundary *|
1971    \*===========================*/
1972    /**
1973     * Tool to set signals at a block boundary, including placing the signal
1974     * icons and setup of Simple Signal Logic for each signal head
1975     * <p>
1976     * Block boundary must be at an Anchor Point on the LayoutEditor panel.
1977     */
1978    //operational variables for Set Signals at Block Boundary tool
1979    private JmriJFrame setSignalsAtBlockBoundaryFrame = null;
1980    private boolean setSignalsAtBlockBoundaryOpenFlag = false;
1981    private boolean setSignalsAtBlockBoundaryFromMenuFlag = false;
1982
1983    private JLabel block1NameLabel = null;
1984    private JLabel block2NameLabel = null;
1985
1986    private final NamedBeanComboBox<Block> block1IDComboBox = new NamedBeanComboBox<>(
1987            InstanceManager.getDefault(BlockManager.class),
1988            null, DisplayOptions.DISPLAYNAME);
1989    private final NamedBeanComboBox<Block> block2IDComboBox = new NamedBeanComboBox<>(
1990            InstanceManager.getDefault(BlockManager.class),
1991            null, DisplayOptions.DISPLAYNAME);
1992
1993    private final NamedBeanComboBox<SignalHead> eastBoundSignalHeadComboBox = new NamedBeanComboBox<>(
1994            InstanceManager.getDefault(SignalHeadManager.class),
1995            null, DisplayOptions.DISPLAYNAME);
1996    private final NamedBeanComboBox<SignalHead> westBoundSignalHeadComboBox = new NamedBeanComboBox<>(
1997            InstanceManager.getDefault(SignalHeadManager.class),
1998            null, DisplayOptions.DISPLAYNAME);
1999
2000    private final JCheckBox setEastBound = new JCheckBox(Bundle.getMessage("PlaceHead"));
2001    private final JCheckBox setupLogicEastBound = new JCheckBox(Bundle.getMessage("SetLogic"));
2002    private final JCheckBox setWestBound = new JCheckBox(Bundle.getMessage("PlaceHead"));
2003    private final JCheckBox setupLogicWestBound = new JCheckBox(Bundle.getMessage("SetLogic"));
2004
2005    private JButton getAnchorSavedSignalHeads = null;
2006    private JButton changeSignalAtBoundaryIcon = null;
2007    private JButton setSignalsAtBlockBoundaryDone = null;
2008    private JButton setSignalsAtBlockBoundaryCancel = null;
2009
2010    private LayoutBlock block1 = null;
2011    private LayoutBlock block2 = null;
2012
2013    private TrackSegment eastTrack = null;
2014    private TrackSegment westTrack = null;
2015
2016    private PositionablePoint boundary = null;
2017    private SignalHead eastBoundHead = null;
2018    private SignalHead westBoundHead = null;
2019
2020    private boolean showWest = true;
2021    private boolean showEast = true;
2022
2023    //display dialog for Set Signals at Block Boundary tool
2024    public void setSignalsAtBlockBoundaryFromMenu(PositionablePoint p,
2025            MultiIconEditor theEditor,
2026            JFrame theFrame) {
2027        boundary = p;
2028
2029        //if this is an edge connector...
2030        if ((p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) && ((p.getLinkedPoint() == null)
2031                || (p.getLinkedPoint().getConnect1() == null))) {
2032            if (p.getConnect1Dir() == Path.EAST || p.getConnect1Dir() == Path.SOUTH) {
2033                showWest = false;
2034            } else {
2035                showEast = false;
2036            }
2037            block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
2038        } else {
2039            block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
2040            block2IDComboBox.setSelectedItem(boundary.getConnect2().getLayoutBlock().getBlock());
2041        }
2042        setSignalsAtBlockBoundaryFromMenuFlag = true;
2043        setSignalsAtBlockBoundary(theEditor, theFrame);
2044        setSignalsAtBlockBoundaryFromMenuFlag = false;
2045    }
2046
2047    public void setSignalsAtBlockBoundary(MultiIconEditor theEditor, JFrame theFrame) {
2048        signalIconEditor = theEditor;
2049        signalFrame = theFrame;
2050
2051        //Initialize if needed
2052        if (setSignalsAtBlockBoundaryFrame == null) {
2053            setSignalsAtBlockBoundaryOpenFlag = false;
2054            setSignalsAtBlockBoundaryFrame = new JmriJFrame(Bundle.getMessage("SignalsAtBoundary"), false, true);
2055            oneFrameToRuleThemAll(setSignalsAtBlockBoundaryFrame);
2056            setSignalsAtBlockBoundaryFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2057            setSignalsAtBlockBoundaryFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtBoundary", true);
2058            setSignalsAtBlockBoundaryFrame.setLocation(70, 30);
2059            Container theContentPane = setSignalsAtBlockBoundaryFrame.getContentPane();
2060            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
2061
2062            JPanel panel11 = new JPanel(new FlowLayout());
2063            block1NameLabel = new JLabel(
2064                    Bundle.getMessage("MakeLabel",
2065                            Bundle.getMessage("BeanNameBlock") + " 1 "));
2066            panel11.add(block1NameLabel);
2067            panel11.add(block1IDComboBox);
2068            block1IDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHintNW"));
2069            theContentPane.add(panel11);
2070
2071            JPanel panel12 = new JPanel(new FlowLayout());
2072            block2NameLabel = new JLabel(
2073                    Bundle.getMessage("MakeLabel",
2074                            Bundle.getMessage("BeanNameBlock")
2075                            + " 2 " + Bundle.getMessage("Name")));
2076            panel12.add(block2NameLabel);
2077            panel12.add(block2IDComboBox);
2078            block2IDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHintSE"));
2079            theContentPane.add(panel12);
2080            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2081
2082            JPanel panel2 = new JPanel(new FlowLayout());
2083            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
2084            panel2.add(shTitle);
2085            panel2.add(new JLabel("   "));
2086            panel2.add(getAnchorSavedSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
2087            getAnchorSavedSignalHeads.addActionListener(this::getSavedAnchorSignals);
2088            getAnchorSavedSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
2089            theContentPane.add(panel2);
2090            if (showEast) {
2091                JPanel panel21 = new JPanel(new FlowLayout());
2092                JLabel eastBoundLabel = new JLabel(Bundle.getMessage("East/SouthBound") + ": ");
2093                panel21.add(eastBoundLabel);
2094                panel21.add(eastBoundSignalHeadComboBox);
2095                theContentPane.add(panel21);
2096                eastBoundSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadEastNameHint"));
2097
2098                JPanel panel22 = new JPanel(new FlowLayout());
2099                panel22.add(new JLabel("   "));
2100                panel22.add(setEastBound);
2101                setEastBound.setToolTipText(Bundle.getMessage("AnchorPlaceHeadHint"));
2102                panel22.add(new JLabel("  "));
2103                if (showWest) {
2104                    panel22.add(setupLogicEastBound);
2105                    setupLogicEastBound.setToolTipText(Bundle.getMessage("SetLogicHint"));
2106                }
2107                theContentPane.add(panel22);
2108            }
2109            if (showWest) {
2110                JPanel panel31 = new JPanel(new FlowLayout());
2111                JLabel westBoundLabel = new JLabel(Bundle.getMessage("West/NorthBound") + ": ");
2112                panel31.add(westBoundLabel);
2113                panel31.add(westBoundSignalHeadComboBox);
2114                theContentPane.add(panel31);
2115                westBoundSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadWestNameHint"));
2116
2117                JPanel panel32 = new JPanel(new FlowLayout());
2118                panel32.add(new JLabel("   "));
2119                panel32.add(setWestBound);
2120                setWestBound.setToolTipText(Bundle.getMessage("AnchorPlaceHeadHint"));
2121                panel32.add(new JLabel("  "));
2122                if (showEast) {
2123                    panel32.add(setupLogicWestBound);
2124                    setupLogicWestBound.setToolTipText(Bundle.getMessage("SetLogicHint"));
2125                }
2126                theContentPane.add(panel32);
2127            }
2128
2129            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2130
2131            JPanel panel6 = new JPanel(new FlowLayout());
2132            panel6.add(changeSignalAtBoundaryIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
2133            changeSignalAtBoundaryIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
2134            changeSignalAtBoundaryIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
2135            panel6.add(new JLabel("   "));
2136            panel6.add(setSignalsAtBlockBoundaryDone = new JButton(Bundle.getMessage("ButtonDone")));
2137            setSignalsAtBlockBoundaryDone.addActionListener(this::setSignalsAtBlockBoundaryDonePressed);
2138            setSignalsAtBlockBoundaryDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
2139
2140            panel6.add(setSignalsAtBlockBoundaryCancel = new JButton(Bundle.getMessage("ButtonCancel")));
2141            setSignalsAtBlockBoundaryCancel.addActionListener(this::setSignalsAtBlockBoundaryCancelPressed);
2142            setSignalsAtBlockBoundaryCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
2143            theContentPane.add(panel6);
2144
2145            //make this button the default button (return or enter activates)
2146            JRootPane rootPane = SwingUtilities.getRootPane(setSignalsAtBlockBoundaryDone);
2147            if (rootPane != null) { //this should never happen but just in case...
2148                rootPane.setDefaultButton(setSignalsAtBlockBoundaryDone);
2149            }
2150
2151            setSignalsAtBlockBoundaryFrame.addWindowListener(new WindowAdapter() {
2152                @Override
2153                public void windowClosing(WindowEvent e) {
2154                    setSignalsAtBlockBoundaryCancelPressed(null);
2155                }
2156            });
2157        }
2158
2159        block1IDComboBox.setVisible(!setSignalsAtBlockBoundaryFromMenuFlag);
2160        block2IDComboBox.setVisible(!setSignalsAtBlockBoundaryFromMenuFlag);
2161
2162        if (setSignalsAtBlockBoundaryFromMenuFlag) {
2163            getSavedAnchorSignals(null);
2164            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
2165                    Bundle.getMessage("BeanNameBlock")
2166                    + " 1 " + Bundle.getMessage("Name"))
2167                    + boundary.getConnect1().getLayoutBlock().getId());
2168            if (boundary.getConnect2() != null) {
2169                block2NameLabel.setText(Bundle.getMessage("MakeLabel",
2170                        Bundle.getMessage("BeanNameBlock")
2171                        + " 2 " + Bundle.getMessage("Name"))
2172                        + boundary.getConnect2().getLayoutBlock().getId());
2173            }
2174        }
2175
2176        if (!setSignalsAtBlockBoundaryOpenFlag) {
2177            setSignalsAtBlockBoundaryFrame.setPreferredSize(null);
2178            setSignalsAtBlockBoundaryFrame.pack();
2179            setSignalsAtBlockBoundaryOpenFlag = true;
2180        }
2181        setSignalsAtBlockBoundaryFrame.setVisible(true);
2182    }   //setSignalsAtBlockBoundary
2183
2184    private void getSavedAnchorSignals(ActionEvent a) {
2185        if (!getBlockInformation()) {
2186            return;
2187        }
2188        eastBoundSignalHeadComboBox.setSelectedItem(boundary.getEastBoundSignalHead());
2189        westBoundSignalHeadComboBox.setSelectedItem(boundary.getWestBoundSignalHead());
2190    }
2191
2192    private void setSignalsAtBlockBoundaryCancelPressed(ActionEvent a) {
2193        setSignalsAtBlockBoundaryOpenFlag = false;
2194        setSignalsAtBlockBoundaryFrame.setVisible(false);
2195    }
2196
2197    private void setSignalsAtBlockBoundaryDonePressed(ActionEvent a) {
2198        if (!getBlockInformation()) {
2199            return;
2200        }
2201        eastBoundHead = getSignalHeadFromEntry(eastBoundSignalHeadComboBox, false, setSignalsAtBlockBoundaryFrame);
2202        westBoundHead = getSignalHeadFromEntry(westBoundSignalHeadComboBox, false, setSignalsAtBlockBoundaryFrame);
2203        if ((eastBoundHead == null) && (westBoundHead == null)) {
2204            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2205                    Bundle.getMessage("SignalsError12"),
2206                    Bundle.getMessage("ErrorTitle"),
2207                    JmriJOptionPane.ERROR_MESSAGE);
2208            return;
2209        }
2210        //place or update signals as requested
2211        String newEastBoundSignalName = eastBoundSignalHeadComboBox.getSelectedItemDisplayName();
2212        if (newEastBoundSignalName == null) {
2213            newEastBoundSignalName = "";
2214        }
2215        if ((eastBoundHead != null) && setEastBound.isSelected()) {
2216            if (isHeadOnPanel(eastBoundHead)
2217                    && (eastBoundHead != getHeadFromName(boundary.getEastBoundSignal()))) {
2218                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2219                        Bundle.getMessage("SignalsError6",
2220                                new Object[]{newEastBoundSignalName}),
2221                        Bundle.getMessage("ErrorTitle"),
2222                        JmriJOptionPane.ERROR_MESSAGE);
2223                return;
2224            } else {
2225                removeSignalHeadFromPanel(boundary.getEastBoundSignal());
2226                placeEastBound();
2227                removeAssignment(eastBoundHead);
2228                boundary.setEastBoundSignal(newEastBoundSignalName);
2229                needRedraw = true;
2230            }
2231        } else if ((eastBoundHead != null)
2232                && (eastBoundHead != getHeadFromName(boundary.getEastBoundSignal()))
2233                && (eastBoundHead != getHeadFromName(boundary.getWestBoundSignal()))) {
2234            if (isHeadOnPanel(eastBoundHead)) {
2235                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2236                        Bundle.getMessage("SignalsError13",
2237                                new Object[]{newEastBoundSignalName}),
2238                        Bundle.getMessage("ErrorTitle"),
2239                        JmriJOptionPane.ERROR_MESSAGE);
2240                return;
2241            } else {
2242                removeSignalHeadFromPanel(boundary.getEastBoundSignal());
2243                removeAssignment(eastBoundHead);
2244                boundary.setEastBoundSignal(newEastBoundSignalName);
2245            }
2246            //} else if ((eastBoundHead != null)
2247            //            && (eastBoundHead == getHeadFromName(boundary.getWestBoundSignal()))) {
2248            //need to figure out what to do in this case.
2249        }
2250        String newWestBoundSignalName = westBoundSignalHeadComboBox.getSelectedItemDisplayName();
2251        if (newWestBoundSignalName == null) {
2252            newWestBoundSignalName = "";
2253        }
2254        if ((westBoundHead != null) && setWestBound.isSelected()) {
2255            if (isHeadOnPanel(westBoundHead)
2256                    && (westBoundHead != getHeadFromName(boundary.getWestBoundSignal()))) {
2257                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2258                        Bundle.getMessage("SignalsError6",
2259                                new Object[]{newWestBoundSignalName}),
2260                        Bundle.getMessage("ErrorTitle"),
2261                        JmriJOptionPane.ERROR_MESSAGE);
2262                return;
2263            } else {
2264                removeSignalHeadFromPanel(boundary.getWestBoundSignal());
2265                placeWestBound();
2266                removeAssignment(westBoundHead);
2267                boundary.setWestBoundSignal(newWestBoundSignalName);
2268                needRedraw = true;
2269            }
2270        } else if ((westBoundHead != null)
2271                && (westBoundHead != getHeadFromName(boundary.getEastBoundSignal()))
2272                && (westBoundHead != getHeadFromName(boundary.getWestBoundSignal()))) {
2273            if (isHeadOnPanel(westBoundHead)) {
2274                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2275                        Bundle.getMessage("SignalsError13",
2276                                new Object[]{newWestBoundSignalName}),
2277                        Bundle.getMessage("ErrorTitle"),
2278                        JmriJOptionPane.ERROR_MESSAGE);
2279                return;
2280            } else {
2281                removeSignalHeadFromPanel(boundary.getWestBoundSignal());
2282                removeAssignment(westBoundHead);
2283                boundary.setWestBoundSignal(newWestBoundSignalName);
2284            }
2285            //} else if ((westBoundHead != null)
2286            //    && (westBoundHead == getHeadFromName(boundary.getEastBoundSignal()))) {
2287            //need to figure out what to do in this case.
2288        }
2289        if ((eastBoundHead != null) && setupLogicEastBound.isSelected()) {
2290            setLogicEastBound();
2291        }
2292        if ((westBoundHead != null) && setupLogicWestBound.isSelected()) {
2293            setLogicWestBound();
2294        }
2295        setSignalsAtBlockBoundaryOpenFlag = false;
2296        setSignalsAtBlockBoundaryFrame.setVisible(false);
2297        if (needRedraw) {
2298            layoutEditor.redrawPanel();
2299            needRedraw = false;
2300            layoutEditor.setDirty();
2301        }
2302    }   //setSignalsAtBlockBoundaryDonePressed
2303
2304    /*
2305    * Do some thing here for end bumpers.
2306     */
2307    private boolean getBlockInformation() {
2308        //might have to do something to trick it with an end bumper
2309        if (!setSignalsAtBlockBoundaryFromMenuFlag) {
2310            block1 = getBlockFromEntry(block1IDComboBox);
2311            if (block1 == null) {
2312                return false;
2313            }
2314            block2 = getBlockFromEntry(block2IDComboBox);
2315            if (block2 == null) {
2316                return false;
2317            }
2318            boundary = null;
2319            for (PositionablePoint p : layoutEditor.getPositionablePoints()) {
2320                if (p.getType() == PositionablePoint.PointType.ANCHOR || p.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
2321                    LayoutBlock bA = null;
2322                    LayoutBlock bB = null;
2323                    if (p.getConnect1() != null) {
2324                        bA = p.getConnect1().getLayoutBlock();
2325                    }
2326                    if (p.getConnect2() != null) {
2327                        bB = p.getConnect2().getLayoutBlock();
2328                    }
2329                    if ((bA != null) && (bB != null) && (bA != bB)) {
2330                        if (((bA == block1) && (bB == block2))
2331                                || ((bA == block2) && (bB == block1))) {
2332                            boundary = p;
2333                            break;
2334                        }
2335                    }
2336                }
2337            }
2338            if (boundary == null) {
2339                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2340                        Bundle.getMessage("SignalsError7"),
2341                        Bundle.getMessage("ErrorTitle"),
2342                        JmriJOptionPane.ERROR_MESSAGE);
2343                return false;
2344            }
2345        }
2346        //set track orientation at boundary
2347        eastTrack = null;
2348        westTrack = null;
2349        TrackSegment track1 = boundary.getConnect1();
2350        Point2D coords1;
2351        if (track1.getConnect1() == boundary) {
2352            coords1 = layoutEditor.getCoords(track1.getConnect2(), track1.getType2());
2353        } else {
2354            coords1 = layoutEditor.getCoords(track1.getConnect1(), track1.getType1());
2355        }
2356        TrackSegment track2 = boundary.getConnect2();
2357
2358        if (boundary.getType() == PositionablePoint.PointType.END_BUMPER) {
2359            return true;
2360        }
2361        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
2362            if (boundary.getConnect1Dir() == Path.EAST || boundary.getConnect1Dir() == Path.SOUTH) {
2363                eastTrack = track2;
2364                westTrack = track1;
2365            } else {
2366                westTrack = track2;
2367                eastTrack = track1;
2368            }
2369            return true;
2370        }
2371        Point2D coords2;
2372        if (track2.getConnect1() == boundary) {
2373            coords2 = layoutEditor.getCoords(track2.getConnect2(), track2.getType2());
2374        } else {
2375            coords2 = layoutEditor.getCoords(track2.getConnect1(), track2.getType1());
2376        }
2377
2378        placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coords2, coords1));
2379
2380        double delX = coords1.getX() - coords2.getX();
2381        double delY = coords1.getY() - coords2.getY();
2382
2383        if (Math.abs(delX) >= Math.abs(delY)) {
2384            if (delX > 0.0) {
2385                eastTrack = track1;
2386                westTrack = track2;
2387            } else {
2388                eastTrack = track2;
2389                westTrack = track1;
2390            }
2391        } else {
2392            if (delY > 0.0) {
2393                eastTrack = track1; //south
2394                westTrack = track2; //north
2395            } else {
2396                eastTrack = track2; //south
2397                westTrack = track1; //north
2398            }
2399        }
2400        return true;
2401    }   //getBlockInformation
2402
2403    @CheckReturnValue
2404    private LayoutBlock getBlockFromEntry(@Nonnull NamedBeanComboBox<Block> blockNameComboBox) {
2405        return getBlockFromEntry(blockNameComboBox.getSelectedItemDisplayName());
2406    }
2407
2408    @CheckReturnValue
2409    private LayoutBlock getBlockFromEntry(@CheckForNull String theBlockName) {
2410        if ((theBlockName == null) || theBlockName.isEmpty()) {
2411            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2412                    Bundle.getMessage("SignalsError9"),
2413                    Bundle.getMessage("ErrorTitle"),
2414                    JmriJOptionPane.ERROR_MESSAGE);
2415            return null;
2416        }
2417        LayoutBlock block = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(theBlockName);
2418        if (block == null) {
2419            block = InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(theBlockName);
2420            if (block == null) {
2421                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2422                        Bundle.getMessage("SignalsError10",
2423                                new Object[]{theBlockName}), Bundle.getMessage("ErrorTitle"),
2424                        JmriJOptionPane.ERROR_MESSAGE);
2425                return null;
2426            }
2427        }
2428        if (!block.isOnPanel(layoutEditor)
2429                && ((boundary == null) || boundary.getType() != PositionablePoint.PointType.EDGE_CONNECTOR)) {
2430            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2431                    Bundle.getMessage("SignalsError11",
2432                            new Object[]{theBlockName}), Bundle.getMessage("ErrorTitle"),
2433                    JmriJOptionPane.ERROR_MESSAGE);
2434            return null;
2435        }
2436        return (block);
2437    }
2438
2439    private void placeEastBound() {
2440        if (testIcon == null) {
2441            testIcon = signalIconEditor.getIcon(0);
2442        }
2443        String signalHeadName = eastBoundSignalHeadComboBox.getSelectedItemDisplayName();
2444        if (signalHeadName == null) {
2445            signalHeadName = "";
2446        }
2447        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
2448
2449        Point2D coords = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
2450        Point2D delta = new Point2D.Double(0.0, +shift);
2451
2452        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
2453        Point2D where = MathUtil.add(coords, delta);
2454        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
2455    }
2456
2457    private void placeWestBound() {
2458        if (testIcon == null) {
2459            testIcon = signalIconEditor.getIcon(0);
2460        }
2461        String signalHeadName = westBoundSignalHeadComboBox.getSelectedItemDisplayName();
2462        if (signalHeadName == null) {
2463            signalHeadName = "";
2464        }
2465        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
2466
2467        Point2D coords = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
2468
2469        Point2D delta = new Point2D.Double(0.0, -shift);
2470        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
2471        Point2D where = MathUtil.add(coords, delta);
2472        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
2473    }
2474
2475    private void setLogicEastBound() {
2476        LayoutBlock eastBlock = eastTrack.getLayoutBlock();
2477        Sensor eastBlockOccupancy = eastBlock.getOccupancySensor();
2478        if (eastBlockOccupancy == null) {
2479            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2480                    Bundle.getMessage("InfoMessage4",
2481                            new Object[]{eastBlock.getUserName()}),
2482                    Bundle.getMessage("MessageTitle"),
2483                    JmriJOptionPane.INFORMATION_MESSAGE);
2484            return;
2485        }
2486        PositionablePoint p = boundary;
2487        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR && eastTrack != boundary.getConnect1()) {
2488            p = boundary.getLinkedPoint();
2489        }
2490        String newEastBoundSignalName = eastBoundSignalHeadComboBox.getSelectedItemDisplayName();
2491        if (newEastBoundSignalName == null) {
2492            newEastBoundSignalName = "";
2493        }
2494        SignalHead nextHead = getNextSignalFromObject(eastTrack,
2495                p, newEastBoundSignalName, setSignalsAtBlockBoundaryFrame);
2496        if ((nextHead == null) && (!reachedEndBumper())) {
2497            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2498                    Bundle.getMessage("InfoMessage5",
2499                            new Object[]{eastBlock.getUserName()}),
2500                    Bundle.getMessage("MessageTitle"),
2501                    JmriJOptionPane.INFORMATION_MESSAGE);
2502            return;
2503        }
2504
2505        if (!initializeBlockBossLogic(newEastBoundSignalName)) {
2506            return;
2507        }
2508        logic.setMode(BlockBossLogic.SINGLEBLOCK);
2509        logic.setSensor1(eastBlockOccupancy.getDisplayName());
2510        if (nextHead != null) {
2511            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
2512        }
2513        if (auxSignal != null) {
2514            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
2515        }
2516        finalizeBlockBossLogic();
2517    }
2518
2519    private void setLogicWestBound() {
2520        LayoutBlock westBlock = westTrack.getLayoutBlock();
2521        Sensor westBlockOccupancy = westBlock.getOccupancySensor();
2522        if (westBlockOccupancy == null) {
2523            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2524                    Bundle.getMessage("InfoMessage4",
2525                            new Object[]{westBlock.getUserName()}),
2526                    Bundle.getMessage("MessageTitle"),
2527                    JmriJOptionPane.INFORMATION_MESSAGE);
2528            return;
2529        }
2530        PositionablePoint p = boundary;
2531        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR && westTrack != boundary.getConnect1()) {
2532            p = boundary.getLinkedPoint();
2533        }
2534        String newWestBoundSignalName = westBoundSignalHeadComboBox.getSelectedItemDisplayName();
2535        if (newWestBoundSignalName == null) {
2536            newWestBoundSignalName = "";
2537        }
2538        SignalHead nextHead = getNextSignalFromObject(westTrack,
2539                p, newWestBoundSignalName, setSignalsAtBlockBoundaryFrame);
2540        if ((nextHead == null) && (!reachedEndBumper())) {
2541            JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
2542                    Bundle.getMessage("InfoMessage5",
2543                            new Object[]{westBlock.getUserName()}),
2544                    Bundle.getMessage("MessageTitle"),
2545                    JmriJOptionPane.INFORMATION_MESSAGE);
2546            return;
2547        }
2548        if (!initializeBlockBossLogic(newWestBoundSignalName)) {
2549            return;
2550        }
2551        logic.setMode(BlockBossLogic.SINGLEBLOCK);
2552        logic.setSensor1(westBlockOccupancy.getDisplayName());
2553        if (nextHead != null) {
2554            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
2555        }
2556        if (auxSignal != null) {
2557            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
2558        }
2559        finalizeBlockBossLogic();
2560    }
2561
2562    /*==========================*\
2563    |* setSignalsAtXoverTurnout *|
2564    \*==========================*/
2565    /**
2566     * Tool to set signals at a double crossover turnout, including placing the
2567     * signal icons and setup of Simple Signal Logic for each signal head
2568     * <p>
2569     * This tool assumes left facing signal head icons have been selected, and
2570     * will rotate the signal head icons accordingly.
2571     * <p>
2572     * This tool will place icons on the outside edge of the turnout.
2573     * <p>
2574     * At least one signal at each of the four connection points is required. A
2575     * second signal at each is optional.
2576     */
2577    //operational variables for Set Signals at Double Crossover Turnout tool
2578    private JmriJFrame setSignalsAtXoverTurnoutFrame = null;
2579    private boolean setSignalsAtXoverTurnoutOpenFlag = false;
2580    private boolean setSignalsAtXoverTurnoutFromMenuFlag = false;
2581
2582    private final NamedBeanComboBox<SignalHead> a1SignalHeadComboBox = new NamedBeanComboBox<>(
2583            InstanceManager.getDefault(SignalHeadManager.class),
2584            null, DisplayOptions.DISPLAYNAME);
2585    private final NamedBeanComboBox<SignalHead> a2SignalHeadComboBox = new NamedBeanComboBox<>(
2586            InstanceManager.getDefault(SignalHeadManager.class),
2587            null, DisplayOptions.DISPLAYNAME);
2588    private final NamedBeanComboBox<SignalHead> b1SignalHeadComboBox = new NamedBeanComboBox<>(
2589            InstanceManager.getDefault(SignalHeadManager.class),
2590            null, DisplayOptions.DISPLAYNAME);
2591    private final NamedBeanComboBox<SignalHead> b2SignalHeadComboBox = new NamedBeanComboBox<>(
2592            InstanceManager.getDefault(SignalHeadManager.class),
2593            null, DisplayOptions.DISPLAYNAME);
2594    private final NamedBeanComboBox<SignalHead> c1SignalHeadComboBox = new NamedBeanComboBox<>(
2595            InstanceManager.getDefault(SignalHeadManager.class),
2596            null, DisplayOptions.DISPLAYNAME);
2597    private final NamedBeanComboBox<SignalHead> c2SignalHeadComboBox = new NamedBeanComboBox<>(
2598            InstanceManager.getDefault(SignalHeadManager.class),
2599            null, DisplayOptions.DISPLAYNAME);
2600    private final NamedBeanComboBox<SignalHead> d1SignalHeadComboBox = new NamedBeanComboBox<>(
2601            InstanceManager.getDefault(SignalHeadManager.class),
2602            null, DisplayOptions.DISPLAYNAME);
2603    private final NamedBeanComboBox<SignalHead> d2SignalHeadComboBox = new NamedBeanComboBox<>(
2604            InstanceManager.getDefault(SignalHeadManager.class),
2605            null, DisplayOptions.DISPLAYNAME);
2606
2607    private final JCheckBox setA1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2608    private final JCheckBox setA2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2609    private final JCheckBox setB1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2610    private final JCheckBox setB2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2611    private final JCheckBox setC1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2612    private final JCheckBox setC2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2613    private final JCheckBox setD1Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2614    private final JCheckBox setD2Head = new JCheckBox(Bundle.getMessage("PlaceHead"));
2615
2616    private final JCheckBox setupA1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2617    private final JCheckBox setupA2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2618    private final JCheckBox setupB1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2619    private final JCheckBox setupB2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2620    private final JCheckBox setupC1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2621    private final JCheckBox setupC2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2622    private final JCheckBox setupD1Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2623    private final JCheckBox setupD2Logic = new JCheckBox(Bundle.getMessage("SetLogic"));
2624
2625    private JButton getSavedXoverSignalHeads = null;
2626    private JButton changeXoverSignalIcon = null;
2627    private JButton setXoverSignalsDone = null;
2628    private JButton setXoverSignalsCancel = null;
2629
2630    private SignalHead a1Head = null;
2631    private SignalHead a2Head = null;
2632    private SignalHead b1Head = null;
2633    private SignalHead b2Head = null;
2634    private SignalHead c1Head = null;
2635    private SignalHead c2Head = null;
2636    private SignalHead d1Head = null;
2637    private SignalHead d2Head = null;
2638
2639    private LayoutTurnout.TurnoutType xoverType = LayoutTurnout.TurnoutType.DOUBLE_XOVER; // changes to RH_XOVER or LH_XOVER as required
2640    private LayoutTurnout.TurnoutType xoverCurr = LayoutTurnout.TurnoutType.NONE;         // Controls creating the frame
2641    private String xoverTurnoutName = "";
2642    private final JLabel xoverTurnoutNameLabel = new JLabel("");
2643
2644    //display dialog for Set Signals at Crossover Turnout tool
2645    public void setSignalsAtXoverTurnoutFromMenu(@Nonnull LayoutTurnout to,
2646            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
2647        layoutTurnout = to;
2648        turnout = to.getTurnout();
2649        xoverType = layoutTurnout.getTurnoutType();
2650        if ((xoverType != LayoutTurnout.TurnoutType.DOUBLE_XOVER) && (xoverType != LayoutTurnout.TurnoutType.RH_XOVER)
2651                && (xoverType != LayoutTurnout.TurnoutType.LH_XOVER)) {
2652            log.error("entered Set Signals at XOver, with a non-crossover turnout");
2653            return;
2654        }
2655        xoverTurnoutName = layoutTurnout.getTurnoutName();
2656        setSignalsAtXoverTurnoutFromMenuFlag = true;
2657        setSignalsAtXoverTurnout(theEditor, theFrame);
2658        setSignalsAtXoverTurnoutFromMenuFlag = false;
2659    }
2660
2661    public void setSignalsAtXoverTurnout(@Nonnull MultiIconEditor theEditor,
2662            @Nonnull JFrame theFrame) {
2663        signalIconEditor = theEditor;
2664        signalFrame = theFrame;
2665
2666        if (!setSignalsAtXoverTurnoutFromMenuFlag) {
2667
2668            List<LayoutTurnout> xovers = new ArrayList<>();
2669            for (LayoutTurnout layoutTurnout : layoutEditor.getLayoutTurnouts()) {
2670                if (layoutTurnout.isTurnoutTypeXover()) {
2671                    xovers.add(layoutTurnout);
2672                }
2673            }
2674            JComboBox<LayoutTurnout> jcb = new JComboBox<>(
2675                    xovers.toArray(new LayoutTurnout[xovers.size()]));
2676            jcb.setEditable(true);
2677            JmriJOptionPane.showMessageDialog(layoutEditor, jcb,
2678                    Bundle.getMessage("MakeLabel",
2679                            Bundle.getMessage("EnterXOverTurnout")),
2680                    JmriJOptionPane.QUESTION_MESSAGE);
2681            LayoutTurnout layoutTurnout = (LayoutTurnout) jcb.getSelectedItem();
2682            xoverTurnoutName = layoutTurnout.getTurnoutName();
2683
2684            if (xoverTurnoutName.length() < 3) {
2685                return;
2686            }
2687        }
2688
2689        if (!getTurnoutInformation(true)) {
2690            return;
2691        }
2692
2693        //Initialize if needed which can be the first time or the crossover type has changed.
2694        if (setSignalsAtXoverTurnoutFrame == null || xoverCurr != xoverType) {
2695            xoverCurr = xoverType;
2696            setSignalsAtXoverTurnoutOpenFlag = false;
2697            setSignalsAtXoverTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalsAtXoverTurnout"), false, true);
2698            oneFrameToRuleThemAll(setSignalsAtXoverTurnoutFrame);
2699            setSignalsAtXoverTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
2700            setSignalsAtXoverTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtXoverTurnout", true);
2701            setSignalsAtXoverTurnoutFrame.setLocation(70, 30);
2702            Container theContentPane = setSignalsAtXoverTurnoutFrame.getContentPane();
2703            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
2704
2705            JPanel panel1 = new JPanel(new FlowLayout());
2706            panel1.add(xoverTurnoutNameLabel);
2707            theContentPane.add(panel1);
2708            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2709
2710            JPanel panel2 = new JPanel(new FlowLayout());
2711            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
2712            panel2.add(shTitle);
2713            panel2.add(new JLabel("   "));
2714            panel2.add(getSavedXoverSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
2715            getSavedXoverSignalHeads.addActionListener(this::xoverTurnoutSignalsGetSaved);
2716            getSavedXoverSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
2717            theContentPane.add(panel2);
2718
2719            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2720            JPanel panel2a = new JPanel(new FlowLayout());
2721            panel2a.add(new JLabel("   "));
2722            panel2a.add(setPlaceAllHeads);
2723            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
2724            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
2725                boolean isSelected = setPlaceAllHeads.isSelected();
2726                //(de)select all checkboxes
2727                setA1Head.setSelected(isSelected);
2728                setA2Head.setSelected(isSelected);
2729                setB1Head.setSelected(isSelected);
2730                setB2Head.setSelected(isSelected);
2731                setC1Head.setSelected(isSelected);
2732                setC2Head.setSelected(isSelected);
2733                setD1Head.setSelected(isSelected);
2734                setD2Head.setSelected(isSelected);
2735            });
2736            panel2a.add(new JLabel("  "));
2737            panel2a.add(setupAllLogic);
2738            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
2739            setupAllLogic.addActionListener((ActionEvent e) -> {
2740                boolean isSelected = setupAllLogic.isSelected();
2741                //(de)select all checkboxes
2742                setupA1Logic.setSelected(isSelected);
2743                setupA2Logic.setSelected(isSelected);
2744                setupB1Logic.setSelected(isSelected);
2745                setupB2Logic.setSelected(isSelected);
2746                setupC1Logic.setSelected(isSelected);
2747                setupC2Logic.setSelected(isSelected);
2748                setupD1Logic.setSelected(isSelected);
2749                setupD2Logic.setSelected(isSelected);
2750            });
2751            theContentPane.add(panel2a);
2752
2753            JPanel panel21 = new JPanel(new FlowLayout());
2754            JLabel a1Label = new JLabel(
2755                    Bundle.getMessage("MakeLabel",
2756                            Bundle.getMessage("XContinuing", "A")));
2757            panel21.add(a1Label);
2758            panel21.add(a1SignalHeadComboBox);
2759            theContentPane.add(panel21);
2760            a1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2761
2762            JPanel panel22 = new JPanel(new FlowLayout());
2763            panel22.add(new JLabel("   "));
2764            panel22.add(setA1Head);
2765            setA1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2766            panel22.add(new JLabel("  "));
2767            panel22.add(setupA1Logic);
2768            setupA1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2769            theContentPane.add(panel22);
2770            if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
2771                JPanel panel23 = new JPanel(new FlowLayout());
2772                JLabel a2Label = new JLabel(Bundle.getMessage("MakeLabel",
2773                        Bundle.getMessage("XDiverging", "A")));
2774                panel23.add(a2Label);
2775                panel23.add(a2SignalHeadComboBox);
2776                theContentPane.add(panel23);
2777                a2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2778                JPanel panel24 = new JPanel(new FlowLayout());
2779                panel24.add(new JLabel("   "));
2780                panel24.add(setA2Head);
2781                setA2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2782                panel24.add(new JLabel("  "));
2783                panel24.add(setupA2Logic);
2784                setupA2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2785                theContentPane.add(panel24);
2786            }
2787
2788            JPanel panel31 = new JPanel(new FlowLayout());
2789            JLabel b1Label = new JLabel(Bundle.getMessage("MakeLabel",
2790                    Bundle.getMessage("XContinuing", "B")));
2791            panel31.add(b1Label);
2792            panel31.add(b1SignalHeadComboBox);
2793            theContentPane.add(panel31);
2794            b1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2795
2796            JPanel panel32 = new JPanel(new FlowLayout());
2797            panel32.add(new JLabel("   "));
2798            panel32.add(setB1Head);
2799            setB1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2800            panel32.add(new JLabel("  "));
2801            panel32.add(setupB1Logic);
2802            setupB1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2803            theContentPane.add(panel32);
2804            if (!(xoverType == LayoutTurnout.TurnoutType.RH_XOVER)) {
2805                JPanel panel33 = new JPanel(new FlowLayout());
2806                JLabel b2Label = new JLabel(Bundle.getMessage("MakeLabel",
2807                        Bundle.getMessage("XDiverging", "B")));
2808                panel33.add(b2Label);
2809                panel33.add(b2SignalHeadComboBox);
2810                theContentPane.add(panel33);
2811                b2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2812                JPanel panel34 = new JPanel(new FlowLayout());
2813                panel34.add(new JLabel("   "));
2814                panel34.add(setB2Head);
2815                setB2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2816                panel34.add(new JLabel("  "));
2817                panel34.add(setupB2Logic);
2818                setupB2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2819                theContentPane.add(panel34);
2820            }
2821
2822            JPanel panel41 = new JPanel(new FlowLayout());
2823            JLabel c1Label = new JLabel(Bundle.getMessage("MakeLabel",
2824                    Bundle.getMessage("XContinuing", "C")));
2825            panel41.add(c1Label);
2826            panel41.add(c1SignalHeadComboBox);
2827            theContentPane.add(panel41);
2828            c1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2829
2830            JPanel panel42 = new JPanel(new FlowLayout());
2831            panel42.add(new JLabel("   "));
2832            panel42.add(setC1Head);
2833            setC1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2834            panel42.add(new JLabel("  "));
2835            panel42.add(setupC1Logic);
2836            setupC1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2837            theContentPane.add(panel42);
2838            if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
2839                JPanel panel43 = new JPanel(new FlowLayout());
2840                JLabel c2Label = new JLabel(Bundle.getMessage("MakeLabel",
2841                        Bundle.getMessage("XDiverging", "C")));
2842                panel43.add(c2Label);
2843                panel43.add(c2SignalHeadComboBox);
2844                theContentPane.add(panel43);
2845                c2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2846                JPanel panel44 = new JPanel(new FlowLayout());
2847                panel44.add(new JLabel("   "));
2848                panel44.add(setC2Head);
2849                setC2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2850                panel44.add(new JLabel("  "));
2851                panel44.add(setupC2Logic);
2852                setupC2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2853                theContentPane.add(panel44);
2854            }
2855
2856            JPanel panel51 = new JPanel(new FlowLayout());
2857            JLabel d1Label = new JLabel(Bundle.getMessage("MakeLabel",
2858                    Bundle.getMessage("XContinuing", "D")));
2859            panel51.add(d1Label);
2860            panel51.add(d1SignalHeadComboBox);
2861            theContentPane.add(panel51);
2862            d1SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2863
2864            JPanel panel52 = new JPanel(new FlowLayout());
2865            panel52.add(new JLabel("   "));
2866            panel52.add(setD1Head);
2867            setD1Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2868            panel52.add(new JLabel("  "));
2869            panel52.add(setupD1Logic);
2870            setupD1Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2871            theContentPane.add(panel52);
2872            if (xoverType != LayoutTurnout.TurnoutType.RH_XOVER) {
2873                JPanel panel53 = new JPanel(new FlowLayout());
2874                JLabel d2Label = new JLabel(Bundle.getMessage("MakeLabel",
2875                        Bundle.getMessage("XDiverging", "D")));
2876                panel53.add(d2Label);
2877                panel53.add(d2SignalHeadComboBox);
2878                theContentPane.add(panel53);
2879                d2SignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
2880                JPanel panel54 = new JPanel(new FlowLayout());
2881                panel54.add(new JLabel("   "));
2882                panel54.add(setD2Head);
2883                setD2Head.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
2884                panel54.add(new JLabel("  "));
2885                panel54.add(setupD2Logic);
2886                setupD2Logic.setToolTipText(Bundle.getMessage("SetLogicHint"));
2887                theContentPane.add(panel54);
2888            }
2889            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
2890
2891            JPanel panel6 = new JPanel(new FlowLayout());
2892            panel6.add(changeXoverSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
2893            changeXoverSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
2894            changeXoverSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
2895            panel6.add(new JLabel("   "));
2896            panel6.add(setXoverSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
2897            setXoverSignalsDone.addActionListener(this::setXoverSignalsDonePressed);
2898            setXoverSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
2899
2900            panel6.add(setXoverSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
2901            setXoverSignalsCancel.addActionListener(this::setXoverSignalsCancelPressed);
2902            setXoverSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
2903            theContentPane.add(panel6);
2904
2905            //make this button the default button (return or enter activates)
2906            JRootPane rootPane = SwingUtilities.getRootPane(setXoverSignalsDone);
2907            if (rootPane != null) {
2908                rootPane.setDefaultButton(setXoverSignalsDone);
2909            }
2910
2911            setSignalsAtXoverTurnoutFrame.addWindowListener(new WindowAdapter() {
2912                @Override
2913                public void windowClosing(WindowEvent e) {
2914                    setXoverSignalsCancelPressed(null);
2915                }
2916            });
2917        }
2918        setPlaceAllHeads.setSelected(false);
2919        setupAllLogic.setSelected(false);
2920
2921        xoverTurnoutNameLabel.setText(Bundle.getMessage("MakeLabel",
2922                Bundle.getMessage("BeanNameTurnout")
2923                + " " + Bundle.getMessage("Name")) + xoverTurnoutName);
2924        xoverType = layoutTurnout.getTurnoutType();
2925
2926        xoverTurnoutSignalsGetSaved(null);
2927
2928        if (!setSignalsAtXoverTurnoutOpenFlag) {
2929            setSignalsAtXoverTurnoutFrame.setPreferredSize(null);
2930            setSignalsAtXoverTurnoutFrame.pack();
2931            setSignalsAtXoverTurnoutOpenFlag = true;
2932        }
2933        setSignalsAtXoverTurnoutFrame.setVisible(true);
2934    }   //setSignalsAtXoverTurnout
2935
2936    private void xoverTurnoutSignalsGetSaved(ActionEvent a) {
2937        a1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA1());
2938        a2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalA2());
2939        b1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalB1());
2940        b2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalB2());
2941        c1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalC1());
2942        c2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalC2());
2943        d1SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalD1());
2944        d2SignalHeadComboBox.setSelectedItem(layoutTurnout.getSignalD2());
2945    }
2946
2947    private void setXoverSignalsCancelPressed(ActionEvent a) {
2948        setSignalsAtXoverTurnoutOpenFlag = false;
2949        setSignalsAtXoverTurnoutFrame.setVisible(false);
2950    }
2951
2952    private void setXoverSignalsDonePressed(ActionEvent a) {
2953        if (!getXoverSignalHeadInformation()) {
2954            return;
2955        }
2956        //place signal icons if requested, and assign signal heads to this turnout
2957        String signalHeadName = a1SignalHeadComboBox.getSelectedItemDisplayName();
2958        if (signalHeadName == null) {
2959            signalHeadName = "";
2960        }
2961        if (setA1Head.isSelected()) {
2962            if (isHeadOnPanel(a1Head)
2963                    && (a1Head != getHeadFromName(layoutTurnout.getSignalA1Name()))) {
2964                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
2965                        Bundle.getMessage("SignalsError6",
2966                                new Object[]{signalHeadName}),
2967                        Bundle.getMessage("ErrorTitle"),
2968                        JmriJOptionPane.ERROR_MESSAGE);
2969                return;
2970            } else {
2971                removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
2972                placeA1();
2973                removeAssignment(a1Head);
2974                layoutTurnout.setSignalA1Name(signalHeadName);
2975                needRedraw = true;
2976            }
2977        } else {
2978            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a1Head, layoutTurnout);
2979            if (assigned == LayoutTurnout.Geometry.NONE) {
2980                if (isHeadOnPanel(a1Head)
2981                        && isHeadAssignedAnywhere(a1Head)) {
2982                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
2983                            Bundle.getMessage("SignalsError8",
2984                                    new Object[]{signalHeadName}),
2985                            Bundle.getMessage("ErrorTitle"),
2986                            JmriJOptionPane.ERROR_MESSAGE);
2987                    return;
2988                } else {
2989                    removeSignalHeadFromPanel(layoutTurnout.getSignalA1Name());
2990                    removeAssignment(a1Head);
2991                    layoutTurnout.setSignalA1Name(signalHeadName);
2992                }
2993                //} else if (assigned != A1) {
2994                //need to figure out what to do in this case.
2995            }
2996        }
2997        signalHeadName = a2SignalHeadComboBox.getSelectedItemDisplayName();
2998        if (signalHeadName == null) {
2999            signalHeadName = "";
3000        }
3001        if ((a2Head != null) && setA2Head.isSelected()) {
3002            if (isHeadOnPanel(a2Head)
3003                    && (a2Head != getHeadFromName(layoutTurnout.getSignalA2Name()))) {
3004                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3005                        Bundle.getMessage("SignalsError6",
3006                                new Object[]{signalHeadName}),
3007                        Bundle.getMessage("ErrorTitle"),
3008                        JmriJOptionPane.ERROR_MESSAGE);
3009                return;
3010            } else {
3011                removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
3012                placeA2();
3013                removeAssignment(a2Head);
3014                layoutTurnout.setSignalA2Name(signalHeadName);
3015                needRedraw = true;
3016            }
3017        } else if (a2Head != null) {
3018            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a2Head, layoutTurnout);
3019            if (assigned == LayoutTurnout.Geometry.NONE) {
3020                if (isHeadOnPanel(a2Head)
3021                        && isHeadAssignedAnywhere(a2Head)) {
3022                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3023                            Bundle.getMessage("SignalsError8",
3024                                    new Object[]{signalHeadName}),
3025                            Bundle.getMessage("ErrorTitle"),
3026                            JmriJOptionPane.ERROR_MESSAGE);
3027                    return;
3028                } else {
3029                    removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
3030                    removeAssignment(a2Head);
3031                    layoutTurnout.setSignalA2Name(signalHeadName);
3032                }
3033                //} else if (assigned != A2) {
3034                //need to figure out what to do in this case.
3035            }
3036        } else { //a2Head known to be null here
3037            removeSignalHeadFromPanel(layoutTurnout.getSignalA2Name());
3038            layoutTurnout.setSignalA2Name("");
3039        }
3040        signalHeadName = b1SignalHeadComboBox.getSelectedItemDisplayName();
3041        if (signalHeadName == null) {
3042            signalHeadName = "";
3043        }
3044        if (setB1Head.isSelected()) {
3045            if (isHeadOnPanel(b1Head)
3046                    && (b1Head != getHeadFromName(layoutTurnout.getSignalB1Name()))) {
3047                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3048                        Bundle.getMessage("SignalsError6",
3049                                new Object[]{signalHeadName}),
3050                        Bundle.getMessage("ErrorTitle"),
3051                        JmriJOptionPane.ERROR_MESSAGE);
3052                return;
3053            } else {
3054                removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
3055                placeB1();
3056                removeAssignment(b1Head);
3057                layoutTurnout.setSignalB1Name(signalHeadName);
3058                needRedraw = true;
3059            }
3060        } else {
3061            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b1Head, layoutTurnout);
3062            if (assigned == LayoutTurnout.Geometry.NONE) {
3063                if (isHeadOnPanel(b1Head)
3064                        && isHeadAssignedAnywhere(b1Head)) {
3065                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3066                            Bundle.getMessage("SignalsError8",
3067                                    new Object[]{signalHeadName}),
3068                            Bundle.getMessage("ErrorTitle"),
3069                            JmriJOptionPane.ERROR_MESSAGE);
3070                    return;
3071                } else {
3072                    removeSignalHeadFromPanel(layoutTurnout.getSignalB1Name());
3073                    removeAssignment(b1Head);
3074                    layoutTurnout.setSignalB1Name(signalHeadName);
3075                }
3076                //} else if (assigned != B1) {
3077                //need to figure out what to do in this case.
3078            }
3079        }
3080        signalHeadName = b2SignalHeadComboBox.getSelectedItemDisplayName();
3081        if (signalHeadName == null) {
3082            signalHeadName = "";
3083        }
3084        if ((b2Head != null) && setB2Head.isSelected()) {
3085            if (isHeadOnPanel(b2Head)
3086                    && (b2Head != getHeadFromName(layoutTurnout.getSignalB2Name()))) {
3087                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3088                        Bundle.getMessage("SignalsError6",
3089                                new Object[]{signalHeadName}),
3090                        Bundle.getMessage("ErrorTitle"),
3091                        JmriJOptionPane.ERROR_MESSAGE);
3092                return;
3093            } else {
3094                removeSignalHeadFromPanel(layoutTurnout.getSignalB2Name());
3095                placeB2();
3096                removeAssignment(b2Head);
3097                layoutTurnout.setSignalB2Name(signalHeadName);
3098                needRedraw = true;
3099            }
3100        } else if (b2Head != null) {
3101            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b2Head, layoutTurnout);
3102            if (assigned == LayoutTurnout.Geometry.NONE) {
3103                if (isHeadOnPanel(b2Head)
3104                        && isHeadAssignedAnywhere(b2Head)) {
3105                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3106                            Bundle.getMessage("SignalsError8",
3107                                    new Object[]{signalHeadName}),
3108                            Bundle.getMessage("ErrorTitle"),
3109                            JmriJOptionPane.ERROR_MESSAGE);
3110                    return;
3111                } else {
3112                    removeSignalHeadFromPanel(layoutTurnout.getSignalB2Name());
3113                    removeAssignment(b2Head);
3114                    layoutTurnout.setSignalB2Name(signalHeadName);
3115                }
3116                //} else if (assigned != B2) {
3117                //need to figure out what to do in this case.
3118            }
3119        } else { //b2Head known to be null here
3120            removeSignalHeadFromPanel(layoutTurnout.getSignalB2Name());
3121            layoutTurnout.setSignalB2Name("");
3122        }
3123        signalHeadName = c1SignalHeadComboBox.getSelectedItemDisplayName();
3124        if (signalHeadName == null) {
3125            signalHeadName = "";
3126        }
3127        if (setC1Head.isSelected()) {
3128            if (isHeadOnPanel(c1Head)
3129                    && (c1Head != getHeadFromName(layoutTurnout.getSignalC1Name()))) {
3130                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3131                        Bundle.getMessage("SignalsError6",
3132                                new Object[]{signalHeadName}),
3133                        Bundle.getMessage("ErrorTitle"),
3134                        JmriJOptionPane.ERROR_MESSAGE);
3135                return;
3136            } else {
3137                removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
3138                placeC1();
3139                removeAssignment(c1Head);
3140                layoutTurnout.setSignalC1Name(signalHeadName);
3141                needRedraw = true;
3142            }
3143        } else {
3144            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c1Head, layoutTurnout);
3145            if (assigned == LayoutTurnout.Geometry.NONE) {
3146                if (isHeadOnPanel(c1Head)
3147                        && isHeadAssignedAnywhere(c1Head)) {
3148                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3149                            Bundle.getMessage("SignalsError8",
3150                                    new Object[]{signalHeadName}),
3151                            Bundle.getMessage("ErrorTitle"),
3152                            JmriJOptionPane.ERROR_MESSAGE);
3153                    return;
3154                } else {
3155                    removeSignalHeadFromPanel(layoutTurnout.getSignalC1Name());
3156                    removeAssignment(c1Head);
3157                    layoutTurnout.setSignalC1Name(signalHeadName);
3158                }
3159                //} else if (assigned != C1) {
3160                //need to figure out what to do in this case.
3161            }
3162        }
3163        signalHeadName = c2SignalHeadComboBox.getSelectedItemDisplayName();
3164        if (signalHeadName == null) {
3165            signalHeadName = "";
3166        }
3167        if ((c2Head != null) && setC2Head.isSelected()) {
3168            if (isHeadOnPanel(c2Head)
3169                    && (c2Head != getHeadFromName(layoutTurnout.getSignalC2Name()))) {
3170                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3171                        Bundle.getMessage("SignalsError6",
3172                                new Object[]{signalHeadName}),
3173                        Bundle.getMessage("ErrorTitle"),
3174                        JmriJOptionPane.ERROR_MESSAGE);
3175                return;
3176            } else {
3177                removeSignalHeadFromPanel(layoutTurnout.getSignalC2Name());
3178                placeC2();
3179                removeAssignment(c2Head);
3180                layoutTurnout.setSignalC2Name(signalHeadName);
3181                needRedraw = true;
3182            }
3183        } else if (c2Head != null) {
3184            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c2Head, layoutTurnout);
3185            if (assigned == LayoutTurnout.Geometry.NONE) {
3186                if (isHeadOnPanel(c2Head)
3187                        && isHeadAssignedAnywhere(c2Head)) {
3188                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3189                            Bundle.getMessage("SignalsError8",
3190                                    new Object[]{signalHeadName}),
3191                            Bundle.getMessage("ErrorTitle"),
3192                            JmriJOptionPane.ERROR_MESSAGE);
3193                    return;
3194                } else {
3195                    removeSignalHeadFromPanel(layoutTurnout.getSignalC2Name());
3196                    removeAssignment(c2Head);
3197                    layoutTurnout.setSignalC2Name(signalHeadName);
3198                }
3199                //} else if (assigned != C2) {
3200                //need to figure out what to do in this case.
3201            }
3202        } else { //c2Head known to be null here
3203            removeSignalHeadFromPanel(layoutTurnout.getSignalC2Name());
3204            layoutTurnout.setSignalC2Name("");
3205        }
3206        signalHeadName = d1SignalHeadComboBox.getSelectedItemDisplayName();
3207        if (signalHeadName == null) {
3208            signalHeadName = "";
3209        }
3210        if (setD1Head.isSelected()) {
3211            if (isHeadOnPanel(d1Head)
3212                    && (d1Head != getHeadFromName(layoutTurnout.getSignalD1Name()))) {
3213                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3214                        Bundle.getMessage("SignalsError6",
3215                                new Object[]{signalHeadName}),
3216                        Bundle.getMessage("ErrorTitle"),
3217                        JmriJOptionPane.ERROR_MESSAGE);
3218                return;
3219            } else {
3220                removeSignalHeadFromPanel(layoutTurnout.getSignalD1Name());
3221                placeD1();
3222                removeAssignment(d1Head);
3223                layoutTurnout.setSignalD1Name(signalHeadName);
3224                needRedraw = true;
3225            }
3226        } else {
3227            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d1Head, layoutTurnout);
3228            if (assigned == LayoutTurnout.Geometry.NONE) {
3229                if (isHeadOnPanel(d1Head)
3230                        && isHeadAssignedAnywhere(d1Head)) {
3231                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3232                            Bundle.getMessage("SignalsError8",
3233                                    new Object[]{signalHeadName}),
3234                            Bundle.getMessage("ErrorTitle"),
3235                            JmriJOptionPane.ERROR_MESSAGE);
3236                    return;
3237                } else {
3238                    removeSignalHeadFromPanel(layoutTurnout.getSignalD1Name());
3239                    removeAssignment(d1Head);
3240                    layoutTurnout.setSignalD1Name(signalHeadName);
3241                }
3242                //} else if (assigned != D1) {
3243                //need to figure out what to do in this case.
3244            }
3245        }
3246        signalHeadName = d2SignalHeadComboBox.getSelectedItemDisplayName();
3247        if (signalHeadName == null) {
3248            signalHeadName = "";
3249        }
3250        if ((d2Head != null) && setD2Head.isSelected()) {
3251            if (isHeadOnPanel(d2Head)
3252                    && (d2Head != getHeadFromName(layoutTurnout.getSignalD2Name()))) {
3253                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3254                        Bundle.getMessage("SignalsError6",
3255                                new Object[]{signalHeadName}),
3256                        Bundle.getMessage("ErrorTitle"),
3257                        JmriJOptionPane.ERROR_MESSAGE);
3258                return;
3259            } else {
3260                removeSignalHeadFromPanel(layoutTurnout.getSignalD2Name());
3261                placeD2();
3262                removeAssignment(d2Head);
3263                layoutTurnout.setSignalD2Name(signalHeadName);
3264                needRedraw = true;
3265            }
3266        } else if (d2Head != null) {
3267            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d2Head, layoutTurnout);
3268            if (assigned == LayoutTurnout.Geometry.NONE) {
3269                if (isHeadOnPanel(d2Head)
3270                        && isHeadAssignedAnywhere(d2Head)) {
3271                    JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3272                            Bundle.getMessage("SignalsError8",
3273                                    new Object[]{signalHeadName}),
3274                            Bundle.getMessage("ErrorTitle"),
3275                            JmriJOptionPane.ERROR_MESSAGE);
3276                    return;
3277                } else {
3278                    removeSignalHeadFromPanel(layoutTurnout.getSignalD2Name());
3279                    removeAssignment(d2Head);
3280                    layoutTurnout.setSignalD2Name(signalHeadName);
3281                }
3282                //} else if (assigned != D2) {
3283                //need to figure out what to do in this case.
3284            }
3285        } else { //d2Head known to be null here
3286            removeSignalHeadFromPanel(layoutTurnout.getSignalD2Name());
3287            layoutTurnout.setSignalD2Name("");
3288        }
3289        //setup logic if requested
3290        if (setupA1Logic.isSelected() || setupA2Logic.isSelected()) {
3291            if (xoverType == LayoutTurnout.TurnoutType.LH_XOVER) {
3292                setLogicXoverContinuing(a1Head, (TrackSegment) layoutTurnout.getConnectB());
3293            } else {
3294                setLogicXover(a1Head, (TrackSegment) layoutTurnout.getConnectB(), a2Head,
3295                        (TrackSegment) layoutTurnout.getConnectC(), setupA1Logic.isSelected(),
3296                        setupA2Logic.isSelected());
3297            }
3298        }
3299        if (setupB1Logic.isSelected() || setupB2Logic.isSelected()) {
3300            if (xoverType == LayoutTurnout.TurnoutType.RH_XOVER) {
3301                setLogicXoverContinuing(b1Head, (TrackSegment) layoutTurnout.getConnectA());
3302            } else {
3303                setLogicXover(b1Head, (TrackSegment) layoutTurnout.getConnectA(), b2Head,
3304                        (TrackSegment) layoutTurnout.getConnectD(), setupB1Logic.isSelected(),
3305                        setupB2Logic.isSelected());
3306            }
3307        }
3308        if (setupC1Logic.isSelected() || setupC2Logic.isSelected()) {
3309            if (xoverType == LayoutTurnout.TurnoutType.LH_XOVER) {
3310                setLogicXoverContinuing(c1Head, (TrackSegment) layoutTurnout.getConnectD());
3311            } else {
3312                setLogicXover(c1Head, (TrackSegment) layoutTurnout.getConnectD(), c2Head,
3313                        (TrackSegment) layoutTurnout.getConnectA(), setupC1Logic.isSelected(),
3314                        setupC2Logic.isSelected());
3315            }
3316        }
3317        if (setupD1Logic.isSelected() || setupD2Logic.isSelected()) {
3318            if (xoverType == LayoutTurnout.TurnoutType.RH_XOVER) {
3319                setLogicXoverContinuing(d1Head, (TrackSegment) layoutTurnout.getConnectC());
3320            } else {
3321                setLogicXover(d1Head, (TrackSegment) layoutTurnout.getConnectC(), d2Head,
3322                        (TrackSegment) layoutTurnout.getConnectB(), setupD1Logic.isSelected(),
3323                        setupD2Logic.isSelected());
3324            }
3325        }
3326        //make sure this layout turnout is not linked to another
3327        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
3328        layoutTurnout.setLinkedTurnoutName("");
3329        //finish up
3330        setSignalsAtXoverTurnoutOpenFlag = false;
3331        setSignalsAtXoverTurnoutFrame.setVisible(false);
3332        if (needRedraw) {
3333            layoutEditor.redrawPanel();
3334            needRedraw = false;
3335            layoutEditor.setDirty();
3336        }
3337    }   //setXoverSignalsDonePressed
3338
3339    private boolean getXoverSignalHeadInformation() {
3340        a1Head = getSignalHeadFromEntry(a1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3341        if (a1Head == null) {
3342            return false;
3343        }
3344        if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
3345            a2Head = getSignalHeadFromEntry(a2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3346        } else {
3347            a2Head = null;
3348        }
3349        b1Head = getSignalHeadFromEntry(b1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3350        if (b1Head == null) {
3351            return false;
3352        }
3353        if (!(xoverType == LayoutTurnout.TurnoutType.RH_XOVER)) {
3354            b2Head = getSignalHeadFromEntry(b2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3355        } else {
3356            b2Head = null;
3357        }
3358        c1Head = getSignalHeadFromEntry(c1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3359        if (c1Head == null) {
3360            return false;
3361        }
3362        if (!(xoverType == LayoutTurnout.TurnoutType.LH_XOVER)) {
3363            c2Head = getSignalHeadFromEntry(c2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3364        } else {
3365            c2Head = null;
3366        }
3367        d1Head = getSignalHeadFromEntry(d1SignalHeadComboBox, true, setSignalsAtXoverTurnoutFrame);
3368        if (d1Head == null) {
3369            return false;
3370        }
3371        if (!(xoverType == LayoutTurnout.TurnoutType.RH_XOVER)) {
3372            d2Head = getSignalHeadFromEntry(d2SignalHeadComboBox, false, setSignalsAtXoverTurnoutFrame);
3373        } else {
3374            d2Head = null;
3375        }
3376        return true;
3377    }
3378
3379    private void placeA1() {
3380        if (testIcon == null) {
3381            testIcon = signalIconEditor.getIcon(0);
3382        }
3383        String signalHeadName = a1SignalHeadComboBox.getSelectedItemDisplayName();
3384        if (signalHeadName == null) {
3385            signalHeadName = "";
3386        }
3387        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3388
3389        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3390
3391        Point2D coordsA = layoutTurnoutView.getCoordsA();
3392        Point2D delta = new Point2D.Double(0.0, +shift);
3393
3394        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3395        Point2D where = MathUtil.add(coordsA, delta);
3396        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3397    }
3398
3399    private void placeA2() {
3400        if (testIcon == null) {
3401            testIcon = signalIconEditor.getIcon(0);
3402        }
3403        String signalHeadName = a2SignalHeadComboBox.getSelectedItemDisplayName();
3404        if (signalHeadName == null) {
3405            signalHeadName = "";
3406        }
3407        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3408
3409        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3410
3411        Point2D coordsA = layoutTurnoutView.getCoordsA();
3412        Point2D delta = new Point2D.Double(-2.0 * shift, +shift);
3413
3414        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3415        Point2D where = MathUtil.add(coordsA, delta);
3416        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3417    }
3418
3419    private void placeB1() {
3420        if (testIcon == null) {
3421            testIcon = signalIconEditor.getIcon(0);
3422        }
3423        String signalHeadName = b1SignalHeadComboBox.getSelectedItemDisplayName();
3424        if (signalHeadName == null) {
3425            signalHeadName = "";
3426        }
3427        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3428
3429        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3430
3431        Point2D coordsB = layoutTurnoutView.getCoordsB();
3432        Point2D delta = new Point2D.Double(-shift, -shift);
3433
3434        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3435        Point2D where = MathUtil.add(coordsB, delta);
3436        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3437    }
3438
3439    private void placeB2() {
3440        if (testIcon == null) {
3441            testIcon = signalIconEditor.getIcon(0);
3442        }
3443        String signalHeadName = b2SignalHeadComboBox.getSelectedItemDisplayName();
3444        if (signalHeadName == null) {
3445            signalHeadName = "";
3446        }
3447        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3448
3449        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3450
3451        Point2D coordsB = layoutTurnoutView.getCoordsB();
3452        Point2D delta = new Point2D.Double(+shift, -shift);
3453
3454        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3455        Point2D where = MathUtil.add(coordsB, delta);
3456        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3457    }
3458
3459    private void placeC1() {
3460        if (testIcon == null) {
3461            testIcon = signalIconEditor.getIcon(0);
3462        }
3463        String signalHeadName = c1SignalHeadComboBox.getSelectedItemDisplayName();
3464        if (signalHeadName == null) {
3465            signalHeadName = "";
3466        }
3467        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3468
3469        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3470
3471        Point2D coordsC = layoutTurnoutView.getCoordsC();
3472        Point2D delta = new Point2D.Double(0.0, -shift);
3473
3474        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3475        Point2D where = MathUtil.add(coordsC, delta);
3476        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3477    }
3478
3479    private void placeC2() {
3480        if (testIcon == null) {
3481            testIcon = signalIconEditor.getIcon(0);
3482        }
3483        String signalHeadName = c2SignalHeadComboBox.getSelectedItemDisplayName();
3484        if (signalHeadName == null) {
3485            signalHeadName = "";
3486        }
3487        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3488
3489        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3490
3491        Point2D coordsC = layoutTurnoutView.getCoordsC();
3492        Point2D delta = new Point2D.Double(+2.0 * shift, -shift);
3493
3494        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3495        Point2D where = MathUtil.add(coordsC, delta);
3496        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
3497    }
3498
3499    private void placeD1() {
3500        if (testIcon == null) {
3501            testIcon = signalIconEditor.getIcon(0);
3502        }
3503        String signalHeadName = d1SignalHeadComboBox.getSelectedItemDisplayName();
3504        if (signalHeadName == null) {
3505            signalHeadName = "";
3506        }
3507        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3508
3509        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3510
3511        Point2D coordsD = layoutTurnoutView.getCoordsD();
3512        Point2D delta = new Point2D.Double(+shift, +shift);
3513
3514        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3515        Point2D where = MathUtil.add(coordsD, delta);
3516        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3517    }
3518
3519    private void placeD2() {
3520        if (testIcon == null) {
3521            testIcon = signalIconEditor.getIcon(0);
3522        }
3523        String signalHeadName = d2SignalHeadComboBox.getSelectedItemDisplayName();
3524        if (signalHeadName == null) {
3525            signalHeadName = "";
3526        }
3527        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
3528
3529        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
3530
3531        Point2D coordsD = layoutTurnoutView.getCoordsD();
3532        Point2D delta = new Point2D.Double(-shift, +shift);
3533
3534        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
3535        Point2D where = MathUtil.add(coordsD, delta);
3536        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
3537    }
3538
3539    @SuppressWarnings("null")
3540    private void setLogicXover(SignalHead head, TrackSegment track, SignalHead secondHead, TrackSegment track2,
3541            boolean setup1, boolean setup2) {
3542        if ((track == null) && setup1) {
3543            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3544                    Bundle.getMessage("InfoMessage7"),
3545                    Bundle.getMessage("MessageTitle"),
3546                    JmriJOptionPane.INFORMATION_MESSAGE);
3547            return;
3548        }
3549        Sensor occupancy = null;
3550        SignalHead nextHead = null;
3551        if ((track != null) && setup1) {
3552            LayoutBlock block = track.getLayoutBlock();
3553            if (block == null) {
3554                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3555                        Bundle.getMessage("InfoMessage6"),
3556                        Bundle.getMessage("MessageTitle"),
3557                        JmriJOptionPane.INFORMATION_MESSAGE);
3558                return;
3559            }
3560            occupancy = block.getOccupancySensor();
3561            if (occupancy == null) {
3562                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3563                        Bundle.getMessage("InfoMessage4",
3564                                new Object[]{block.getUserName()}),
3565                        Bundle.getMessage("MessageTitle"),
3566                        JmriJOptionPane.INFORMATION_MESSAGE);
3567                return;
3568            }
3569            nextHead = getNextSignalFromObject(track,
3570                    layoutTurnout, head.getSystemName(), setSignalsAtXoverTurnoutFrame);
3571            if ((nextHead == null) && (!reachedEndBumper())) {
3572                JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3573                        Bundle.getMessage("InfoMessage5",
3574                                new Object[]{block.getUserName()}),
3575                        Bundle.getMessage("MessageTitle"),
3576                        JmriJOptionPane.INFORMATION_MESSAGE);
3577                return;
3578            }
3579            if (secondHead != null) {
3580                if (!initializeBlockBossLogic(head.getDisplayName())) {
3581                    return;
3582                }
3583                logic.setMode(BlockBossLogic.TRAILINGMAIN);
3584                logic.setTurnout(turnout.getDisplayName());
3585                logic.setSensor1(occupancy.getDisplayName());
3586                if (nextHead != null) {
3587                    logic.setWatchedSignal1(nextHead.getDisplayName(), false);
3588                }
3589                if (auxSignal != null) {
3590                    logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
3591                }
3592                finalizeBlockBossLogic();
3593            }
3594        }
3595        if ((secondHead != null) && !setup2) {
3596            return;
3597        }
3598        SignalHead savedAuxSignal = auxSignal;
3599        if (track2 == null) {
3600            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3601                    Bundle.getMessage("InfoMessage7"),
3602                    Bundle.getMessage("MessageTitle"),
3603                    JmriJOptionPane.INFORMATION_MESSAGE);
3604            return;
3605        }
3606        LayoutBlock block2 = track2.getLayoutBlock();
3607        if (block2 == null) {
3608            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3609                    Bundle.getMessage("InfoMessage6"),
3610                    Bundle.getMessage("MessageTitle"),
3611                    JmriJOptionPane.INFORMATION_MESSAGE);
3612            return;
3613        }
3614        Sensor occupancy2 = block2.getOccupancySensor();
3615        if (occupancy2 == null) {
3616            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3617                    Bundle.getMessage("InfoMessage4",
3618                            new Object[]{block2.getUserName()}),
3619                    Bundle.getMessage("MessageTitle"),
3620                    JmriJOptionPane.INFORMATION_MESSAGE);
3621            return;
3622        }
3623        String signalHeadName = head.getSystemName();
3624        if (secondHead != null) {
3625            signalHeadName = secondHead.getSystemName();
3626        }
3627        SignalHead nextHead2 = getNextSignalFromObject(track2,
3628                layoutTurnout, signalHeadName, setSignalsAtXoverTurnoutFrame);
3629        if ((nextHead2 == null) && (!reachedEndBumper())) {
3630            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3631                    Bundle.getMessage("InfoMessage5",
3632                            new Object[]{block2.getUserName()}),
3633                    Bundle.getMessage("MessageTitle"),
3634                    JmriJOptionPane.INFORMATION_MESSAGE);
3635            return;
3636        }
3637        if ((secondHead == null) && (track != null) && setup1) {
3638            if (!initializeBlockBossLogic(head.getDisplayName())) {
3639                return;
3640            }
3641            logic.setMode(BlockBossLogic.FACING);
3642            logic.setTurnout(turnout.getDisplayName());
3643            logic.setWatchedSensor1(occupancy.getDisplayName());
3644            logic.setWatchedSensor2(occupancy2.getDisplayName());
3645            if (nextHead != null) {
3646                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
3647            }
3648            if (savedAuxSignal != null) {
3649                logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
3650            }
3651            if (nextHead2 != null) {
3652                logic.setWatchedSignal2(nextHead2.getDisplayName());
3653            }
3654            if (auxSignal != null) {
3655                logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
3656            }
3657            logic.setLimitSpeed2(true);
3658            finalizeBlockBossLogic();
3659        } else if ((secondHead != null) && setup2) {
3660            if (!initializeBlockBossLogic(secondHead.getDisplayName())) {
3661                return;
3662            }
3663            logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
3664            logic.setTurnout(turnout.getDisplayName());
3665            logic.setSensor1(occupancy2.getDisplayName());
3666            if (nextHead2 != null) {
3667                logic.setWatchedSignal1(nextHead2.getDisplayName(), false);
3668            }
3669            if (auxSignal != null) {
3670                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
3671            }
3672            logic.setLimitSpeed2(true);
3673            finalizeBlockBossLogic();
3674        }
3675    }   //setLogicXover
3676
3677    private void setLogicXoverContinuing(SignalHead head, TrackSegment track) {
3678        if (track == null) {
3679            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3680                    Bundle.getMessage("InfoMessage7"),
3681                    Bundle.getMessage("MessageTitle"),
3682                    JmriJOptionPane.INFORMATION_MESSAGE);
3683            return;
3684        }
3685        LayoutBlock block = track.getLayoutBlock();
3686        if (block == null) {
3687            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3688                    Bundle.getMessage("InfoMessage6"),
3689                    Bundle.getMessage("MessageTitle"),
3690                    JmriJOptionPane.INFORMATION_MESSAGE);
3691            return;
3692        }
3693        Sensor occupancy = block.getOccupancySensor();
3694        if (occupancy == null) {
3695            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3696                    Bundle.getMessage("InfoMessage4",
3697                            new Object[]{block.getUserName()}),
3698                    Bundle.getMessage("MessageTitle"),
3699                    JmriJOptionPane.INFORMATION_MESSAGE);
3700            return;
3701        }
3702        SignalHead nextHead = getNextSignalFromObject(track,
3703                layoutTurnout, head.getSystemName(), setSignalsAtXoverTurnoutFrame);
3704        if ((nextHead == null) && (!reachedEndBumper())) {
3705            JmriJOptionPane.showMessageDialog(setSignalsAtXoverTurnoutFrame,
3706                    Bundle.getMessage("InfoMessage5",
3707                            new Object[]{block.getUserName()}),
3708                    Bundle.getMessage("MessageTitle"),
3709                    JmriJOptionPane.INFORMATION_MESSAGE);
3710            return;
3711        }
3712        if (!initializeBlockBossLogic(head.getDisplayName())) {
3713            return;
3714        }
3715        logic.setMode(BlockBossLogic.TRAILINGMAIN);
3716        logic.setTurnout(turnout.getDisplayName());
3717        logic.setSensor1(occupancy.getDisplayName());
3718        if (nextHead != null) {
3719            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
3720        }
3721        if (auxSignal != null) {
3722            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
3723        }
3724        finalizeBlockBossLogic();
3725    }   //setLogicXoverContinuing
3726
3727    /*=======================*\
3728    |* setSignalsAtLevelXing *|
3729    \*=======================*/
3730    /**
3731     * Tool to set signals at a level crossing, including placing the signal
3732     * icons and setup of Simple Signal Logic for each signal head
3733     * <p>
3734     * This tool assumes left facing signal head icons have been selected, and
3735     * will rotate the signal head icons accordingly.
3736     * <p>
3737     * This tool will place icons on the right side of each track.
3738     * <p>
3739     * Both tracks do not need to be signalled. If one signal for a track, A-C
3740     * or B-D, the other must also be present.
3741     * <p>
3742     * Some user adjustment of turnout positions may be needed.
3743     */
3744    //operational variables for Set Signals at Level Crossing tool
3745    private JmriJFrame setSignalsAtLevelXingFrame = null;
3746    private boolean setSignalsAtLevelXingOpenFlag = false;
3747    private boolean setSignalsAtLevelXingFromMenuFlag = false;
3748
3749    private JLabel blockACNameLabel = null;
3750    private JLabel blockBDNameLabel = null;
3751
3752    private final NamedBeanComboBox<Block> blockACComboBox = new NamedBeanComboBox<>(
3753            InstanceManager.getDefault(BlockManager.class),
3754            null, DisplayOptions.DISPLAYNAME);
3755    private final NamedBeanComboBox<Block> blockBDComboBox = new NamedBeanComboBox<>(
3756            InstanceManager.getDefault(BlockManager.class),
3757            null, DisplayOptions.DISPLAYNAME);
3758
3759    private final NamedBeanComboBox<SignalHead> aSignalHeadComboBox = new NamedBeanComboBox<>(
3760            InstanceManager.getDefault(SignalHeadManager.class),
3761            null, DisplayOptions.DISPLAYNAME);
3762    private final NamedBeanComboBox<SignalHead> bSignalHeadComboBox = new NamedBeanComboBox<>(
3763            InstanceManager.getDefault(SignalHeadManager.class),
3764            null, DisplayOptions.DISPLAYNAME);
3765    private final NamedBeanComboBox<SignalHead> cSignalHeadComboBox = new NamedBeanComboBox<>(
3766            InstanceManager.getDefault(SignalHeadManager.class),
3767            null, DisplayOptions.DISPLAYNAME);
3768    private final NamedBeanComboBox<SignalHead> dSignalHeadComboBox = new NamedBeanComboBox<>(
3769            InstanceManager.getDefault(SignalHeadManager.class),
3770            null, DisplayOptions.DISPLAYNAME);
3771
3772    private final JCheckBox setAHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3773    private final JCheckBox setBHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3774    private final JCheckBox setCHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3775    private final JCheckBox setDHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
3776
3777    private final JCheckBox setupALogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3778    private final JCheckBox setupBLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3779    private final JCheckBox setupCLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3780    private final JCheckBox setupDLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
3781
3782    private JButton getSavedXingSignalHeads = null;
3783    private JButton changeXingSignalIcon = null;
3784    private JButton setXingSignalsDone = null;
3785    private JButton setXingSignalsCancel = null;
3786
3787    private LevelXing levelXing = null;
3788
3789    private SignalHead aHead = null;
3790    private SignalHead bHead = null;
3791    private SignalHead cHead = null;
3792    private SignalHead dHead = null;
3793
3794    //display dialog for Set Signals at Level Crossing tool
3795    public void setSignalsAtLevelXingFromMenu(@Nonnull LevelXing xing,
3796            @Nonnull MultiIconEditor theEditor,
3797            @Nonnull JFrame theFrame) {
3798        levelXing = xing;
3799        blockACComboBox.setSelectedItem(levelXing.getLayoutBlockAC().getBlock());
3800        blockBDComboBox.setSelectedItem(levelXing.getLayoutBlockBD().getBlock());
3801        setSignalsAtLevelXingFromMenuFlag = true;
3802        setSignalsAtLevelXing(theEditor, theFrame);
3803        setSignalsAtLevelXingFromMenuFlag = false;
3804    }
3805
3806    public void setSignalsAtLevelXing(@Nonnull MultiIconEditor theEditor,
3807            @Nonnull JFrame theFrame) {
3808        signalIconEditor = theEditor;
3809        signalFrame = theFrame;
3810
3811        //Initialize if needed
3812        if (setSignalsAtLevelXingFrame == null) {
3813            setSignalsAtLevelXingOpenFlag = false;
3814            setSignalsAtLevelXingFrame = new JmriJFrame(Bundle.getMessage("SignalsAtLevelXing"), false, true);
3815            oneFrameToRuleThemAll(setSignalsAtLevelXingFrame);
3816            setSignalsAtLevelXingFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
3817            setSignalsAtLevelXingFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtLevelXing", true);
3818            setSignalsAtLevelXingFrame.setLocation(70, 30);
3819            Container theContentPane = setSignalsAtLevelXingFrame.getContentPane();
3820            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
3821
3822            JPanel panel11 = new JPanel(new FlowLayout());
3823            blockACNameLabel = new JLabel(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " AC")));
3824            panel11.add(blockACNameLabel);
3825            panel11.add(blockACComboBox);
3826            blockACComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
3827            theContentPane.add(panel11);
3828
3829            JPanel panel12 = new JPanel(new FlowLayout());
3830            blockBDNameLabel = new JLabel(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " BD")));
3831            panel12.add(blockBDNameLabel);
3832            panel12.add(blockBDComboBox);
3833            blockBDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
3834
3835            theContentPane.add(panel12);
3836            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
3837
3838            JPanel panel2 = new JPanel(new FlowLayout());
3839            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
3840            panel2.add(shTitle);
3841            panel2.add(new JLabel("   "));
3842            panel2.add(getSavedXingSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
3843            getSavedXingSignalHeads.addActionListener(this::xingSignalsGetSaved);
3844            getSavedXingSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
3845            theContentPane.add(panel2);
3846
3847            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
3848            JPanel panel2a = new JPanel(new FlowLayout());
3849            panel2a.add(new JLabel("   "));
3850            panel2a.add(setPlaceAllHeads);
3851            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
3852            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
3853                boolean isSelected = setPlaceAllHeads.isSelected();
3854                //(de)select all checkboxes
3855                setAHead.setSelected(isSelected);
3856                setBHead.setSelected(isSelected);
3857                setCHead.setSelected(isSelected);
3858                setDHead.setSelected(isSelected);
3859            });
3860            panel2a.add(new JLabel("  "));
3861            panel2a.add(setupAllLogic);
3862            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
3863            setupAllLogic.addActionListener((ActionEvent e) -> {
3864                boolean isSelected = setupAllLogic.isSelected();
3865                //(de)select all checkboxes
3866                setupALogic.setSelected(isSelected);
3867                setupBLogic.setSelected(isSelected);
3868                setupCLogic.setSelected(isSelected);
3869                setupDLogic.setSelected(isSelected);
3870            });
3871            theContentPane.add(panel2a);
3872
3873            JPanel panel21 = new JPanel(new FlowLayout());
3874            JLabel aLabel = new JLabel(Bundle.getMessage("MakeLabel",
3875                    Bundle.getMessage("TrackXConnect", "A")));
3876            panel21.add(aLabel);
3877            panel21.add(aSignalHeadComboBox);
3878            theContentPane.add(panel21);
3879            aSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3880
3881            JPanel panel22 = new JPanel(new FlowLayout());
3882            panel22.add(new JLabel("   "));
3883            panel22.add(setAHead);
3884            setAHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3885            panel22.add(new JLabel("  "));
3886            panel22.add(setupALogic);
3887            setupALogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3888            theContentPane.add(panel22);
3889
3890            JPanel panel31 = new JPanel(new FlowLayout());
3891            JLabel bLabel = new JLabel(Bundle.getMessage("MakeLabel",
3892                    Bundle.getMessage("TrackXConnect", "B")));
3893            panel31.add(bLabel);
3894            panel31.add(bSignalHeadComboBox);
3895            theContentPane.add(panel31);
3896            bSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3897
3898            JPanel panel32 = new JPanel(new FlowLayout());
3899            panel32.add(new JLabel("   "));
3900            panel32.add(setBHead);
3901            setBHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3902            panel32.add(new JLabel("  "));
3903            panel32.add(setupBLogic);
3904            setupBLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3905            theContentPane.add(panel32);
3906
3907            JPanel panel41 = new JPanel(new FlowLayout());
3908            JLabel cLabel = new JLabel(Bundle.getMessage("MakeLabel",
3909                    Bundle.getMessage("TrackXConnect", "C")));
3910            panel41.add(cLabel);
3911            panel41.add(cSignalHeadComboBox);
3912            theContentPane.add(panel41);
3913            cSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3914
3915            JPanel panel42 = new JPanel(new FlowLayout());
3916            panel42.add(new JLabel("   "));
3917            panel42.add(setCHead);
3918            setCHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3919            panel42.add(new JLabel("  "));
3920            panel42.add(setupCLogic);
3921            setupCLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3922            theContentPane.add(panel42);
3923
3924            JPanel panel51 = new JPanel(new FlowLayout());
3925            JLabel dLabel = new JLabel(Bundle.getMessage("MakeLabel",
3926                    Bundle.getMessage("TrackXConnect", "D")));
3927            panel51.add(dLabel);
3928            panel51.add(dSignalHeadComboBox);
3929            theContentPane.add(panel51);
3930            dSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
3931
3932            JPanel panel52 = new JPanel(new FlowLayout());
3933            panel52.add(new JLabel("   "));
3934            panel52.add(setDHead);
3935            setDHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
3936            panel52.add(new JLabel("  "));
3937            panel52.add(setupDLogic);
3938            setupDLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
3939            theContentPane.add(panel52);
3940            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
3941
3942            JPanel panel6 = new JPanel(new FlowLayout());
3943            panel6.add(changeXingSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
3944            changeXingSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
3945            changeXingSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
3946            panel6.add(new JLabel("   "));
3947            panel6.add(setXingSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
3948            setXingSignalsDone.addActionListener(this::setXingSignalsDonePressed);
3949            setXingSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
3950
3951            panel6.add(setXingSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
3952            setXingSignalsCancel.addActionListener(this::setXingSignalsCancelPressed);
3953            setXingSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
3954            theContentPane.add(panel6);
3955
3956            //make this button the default button (return or enter activates)
3957            JRootPane rootPane = SwingUtilities.getRootPane(setXingSignalsDone);
3958            if (rootPane != null) {
3959                rootPane.setDefaultButton(setXingSignalsDone);
3960            }
3961
3962            setSignalsAtLevelXingFrame.addWindowListener(new WindowAdapter() {
3963                @Override
3964                public void windowClosing(WindowEvent e) {
3965                    setXingSignalsCancelPressed(null);
3966                }
3967            });
3968        }
3969
3970        aSignalHeadComboBox.setSelectedItem(null);
3971        bSignalHeadComboBox.setSelectedItem(null);
3972        cSignalHeadComboBox.setSelectedItem(null);
3973        dSignalHeadComboBox.setSelectedItem(null);
3974
3975        setPlaceAllHeads.setSelected(false);
3976        setupAllLogic.setSelected(false);
3977
3978        blockACComboBox.setVisible(!setSignalsAtLevelXingFromMenuFlag);
3979        blockBDComboBox.setVisible(!setSignalsAtLevelXingFromMenuFlag);
3980
3981        if (setSignalsAtLevelXingFromMenuFlag) {
3982            blockACNameLabel.setText(Bundle.getMessage("MakeLabel",
3983                    (Bundle.getMessage("BeanNameBlock") + " AC"))
3984                    + levelXing.getBlockNameAC());
3985            blockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
3986                    (Bundle.getMessage("BeanNameBlock") + " BD"))
3987                    + levelXing.getBlockNameBD());
3988            xingSignalsGetSaved(null);
3989        } else {
3990            blockACNameLabel.setText(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " AC")));
3991            blockBDNameLabel.setText(Bundle.getMessage("MakeLabel", (Bundle.getMessage("BeanNameBlock") + " BD")));
3992        }
3993
3994        if (!setSignalsAtLevelXingOpenFlag) {
3995            setSignalsAtLevelXingFrame.setPreferredSize(null);
3996            setSignalsAtLevelXingFrame.pack();
3997            setSignalsAtLevelXingOpenFlag = true;
3998        }
3999
4000        setSignalsAtLevelXingFrame.setVisible(true);
4001    }   //setSignalsAtLevelXing
4002
4003    private void xingSignalsGetSaved(ActionEvent a) {
4004        if (!getLevelCrossingInformation()) {
4005            return;
4006        }
4007        aSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTA));
4008        bSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTB));
4009        cSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTC));
4010        dSignalHeadComboBox.setSelectedItem(levelXing.getSignalHead(LevelXing.Geometry.POINTD));
4011    }
4012
4013    private void setXingSignalsCancelPressed(ActionEvent a) {
4014        setSignalsAtLevelXingOpenFlag = false;
4015        setSignalsAtLevelXingFrame.setVisible(false);
4016    }
4017
4018    private void setXingSignalsDonePressed(ActionEvent a) {
4019        if (!getLevelCrossingInformation()) {
4020            return;
4021        }
4022        if (!getXingSignalHeadInformation()) {
4023            return;
4024        }
4025
4026        //place or update signals as requested
4027        String signalName = aSignalHeadComboBox.getSelectedItemDisplayName();
4028        if (signalName == null) {
4029            signalName = "";
4030        }
4031        if ((aHead != null) && setAHead.isSelected()) {
4032            if (isHeadOnPanel(aHead)
4033                    && (aHead != getHeadFromName(levelXing.getSignalAName()))) {
4034                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4035                        Bundle.getMessage("SignalsError6",
4036                                new Object[]{signalName}),
4037                        Bundle.getMessage("ErrorTitle"),
4038                        JmriJOptionPane.ERROR_MESSAGE);
4039                return;
4040            } else {
4041                removeSignalHeadFromPanel(levelXing.getSignalAName());
4042                placeXingA();
4043                removeAssignment(aHead);
4044                levelXing.setSignalAName(signalName);
4045                needRedraw = true;
4046            }
4047        } else if ((aHead != null)
4048                && (aHead != getHeadFromName(levelXing.getSignalAName()))
4049                && (aHead != getHeadFromName(levelXing.getSignalBName()))
4050                && (aHead != getHeadFromName(levelXing.getSignalCName()))
4051                && (aHead != getHeadFromName(levelXing.getSignalDName()))) {
4052            if (isHeadOnPanel(aHead)) {
4053                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4054                        Bundle.getMessage("SignalsError13",
4055                                new Object[]{signalName}),
4056                        Bundle.getMessage("ErrorTitle"),
4057                        JmriJOptionPane.ERROR_MESSAGE);
4058                return;
4059            } else {
4060                removeSignalHeadFromPanel(levelXing.getSignalAName());
4061                removeAssignment(aHead);
4062                levelXing.setSignalAName(signalName);
4063            }
4064        } else if ((aHead != null)
4065                && ((aHead == getHeadFromName(levelXing.getSignalBName()))
4066                || (aHead == getHeadFromName(levelXing.getSignalCName()))
4067                || (aHead == getHeadFromName(levelXing.getSignalDName())))) {
4068            //need to figure out what to do in this case.
4069            log.trace("need to figure out what to do in this case.");
4070        } else if (aHead == null) {
4071            removeSignalHeadFromPanel(levelXing.getSignalAName());
4072            levelXing.setSignalAName("");
4073        }
4074        signalName = bSignalHeadComboBox.getSelectedItemDisplayName();
4075        if (signalName == null) {
4076            signalName = "";
4077        }
4078        if ((bHead != null) && setBHead.isSelected()) {
4079            if (isHeadOnPanel(bHead)
4080                    && (bHead != getHeadFromName(levelXing.getSignalBName()))) {
4081                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4082                        Bundle.getMessage("SignalsError6",
4083                                new Object[]{signalName}),
4084                        Bundle.getMessage("ErrorTitle"),
4085                        JmriJOptionPane.ERROR_MESSAGE);
4086                return;
4087            } else {
4088                removeSignalHeadFromPanel(levelXing.getSignalBName());
4089                placeXingB();
4090                removeAssignment(bHead);
4091                levelXing.setSignalBName(signalName);
4092                needRedraw = true;
4093            }
4094        } else if ((bHead != null)
4095                && (bHead != getHeadFromName(levelXing.getSignalAName()))
4096                && (bHead != getHeadFromName(levelXing.getSignalBName()))
4097                && (bHead != getHeadFromName(levelXing.getSignalCName()))
4098                && (bHead != getHeadFromName(levelXing.getSignalDName()))) {
4099            if (isHeadOnPanel(bHead)) {
4100                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4101                        Bundle.getMessage("SignalsError13",
4102                                new Object[]{signalName}),
4103                        Bundle.getMessage("ErrorTitle"),
4104                        JmriJOptionPane.ERROR_MESSAGE);
4105                return;
4106            } else {
4107                removeSignalHeadFromPanel(levelXing.getSignalBName());
4108                removeAssignment(bHead);
4109                levelXing.setSignalBName(signalName);
4110            }
4111        } else if ((bHead != null)
4112                && ((bHead == getHeadFromName(levelXing.getSignalAName()))
4113                || (bHead == getHeadFromName(levelXing.getSignalCName()))
4114                || (bHead == getHeadFromName(levelXing.getSignalDName())))) {
4115            //need to figure out what to do in this case.
4116            log.trace("need to figure out what to do in this case.");
4117        } else if (bHead == null) {
4118            removeSignalHeadFromPanel(levelXing.getSignalBName());
4119            levelXing.setSignalBName("");
4120        }
4121        signalName = cSignalHeadComboBox.getSelectedItemDisplayName();
4122        if (signalName == null) {
4123            signalName = "";
4124        }
4125        if ((cHead != null) && setCHead.isSelected()) {
4126            if (isHeadOnPanel(cHead)
4127                    && (cHead != getHeadFromName(levelXing.getSignalCName()))) {
4128                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4129                        Bundle.getMessage("SignalsError6",
4130                                new Object[]{signalName}),
4131                        Bundle.getMessage("ErrorTitle"),
4132                        JmriJOptionPane.ERROR_MESSAGE);
4133                return;
4134            } else {
4135                removeSignalHeadFromPanel(levelXing.getSignalCName());
4136                placeXingC();
4137                removeAssignment(cHead);
4138                levelXing.setSignalCName(signalName);
4139                needRedraw = true;
4140            }
4141        } else if ((cHead != null)
4142                && (cHead != getHeadFromName(levelXing.getSignalAName()))
4143                && (cHead != getHeadFromName(levelXing.getSignalBName()))
4144                && (cHead != getHeadFromName(levelXing.getSignalCName()))
4145                && (cHead != getHeadFromName(levelXing.getSignalDName()))) {
4146            if (isHeadOnPanel(cHead)) {
4147                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4148                        Bundle.getMessage("SignalsError13",
4149                                new Object[]{signalName}),
4150                        Bundle.getMessage("ErrorTitle"),
4151                        JmriJOptionPane.ERROR_MESSAGE);
4152                return;
4153            } else {
4154                removeSignalHeadFromPanel(levelXing.getSignalCName());
4155                removeAssignment(cHead);
4156                levelXing.setSignalCName(signalName);
4157            }
4158        } else if ((cHead != null)
4159                && ((cHead == getHeadFromName(levelXing.getSignalBName()))
4160                || (cHead == getHeadFromName(levelXing.getSignalAName()))
4161                || (cHead == getHeadFromName(levelXing.getSignalDName())))) {
4162            //need to figure out what to do in this case.
4163            log.trace("need to figure out what to do in this case.");
4164        } else if (cHead == null) {
4165            removeSignalHeadFromPanel(levelXing.getSignalCName());
4166            levelXing.setSignalCName("");
4167        }
4168        signalName = dSignalHeadComboBox.getSelectedItemDisplayName();
4169        if (signalName == null) {
4170            signalName = "";
4171        }
4172        if ((dHead != null) && setDHead.isSelected()) {
4173            if (isHeadOnPanel(dHead)
4174                    && (dHead != getHeadFromName(levelXing.getSignalDName()))) {
4175                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4176                        Bundle.getMessage("SignalsError6",
4177                                new Object[]{signalName}),
4178                        Bundle.getMessage("ErrorTitle"),
4179                        JmriJOptionPane.ERROR_MESSAGE);
4180                return;
4181            } else {
4182                removeSignalHeadFromPanel(levelXing.getSignalDName());
4183                placeXingD();
4184                removeAssignment(dHead);
4185                levelXing.setSignalDName(signalName);
4186                needRedraw = true;
4187            }
4188        } else if ((dHead != null)
4189                && (dHead != getHeadFromName(levelXing.getSignalAName()))
4190                && (dHead != getHeadFromName(levelXing.getSignalBName()))
4191                && (dHead != getHeadFromName(levelXing.getSignalCName()))
4192                && (dHead != getHeadFromName(levelXing.getSignalDName()))) {
4193            if (isHeadOnPanel(dHead)) {
4194                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4195                        Bundle.getMessage("SignalsError13",
4196                                new Object[]{signalName}),
4197                        Bundle.getMessage("ErrorTitle"),
4198                        JmriJOptionPane.ERROR_MESSAGE);
4199                return;
4200            } else {
4201                removeSignalHeadFromPanel(levelXing.getSignalDName());
4202                removeAssignment(dHead);
4203                levelXing.setSignalDName(signalName);
4204            }
4205        } else if ((dHead != null)
4206                && ((dHead == getHeadFromName(levelXing.getSignalBName()))
4207                || (dHead == getHeadFromName(levelXing.getSignalCName()))
4208                || (dHead == getHeadFromName(levelXing.getSignalAName())))) {
4209            //need to figure out what to do in this case.
4210            log.trace("need to figure out what to do in this case.");
4211        } else if (dHead == null) {
4212            removeSignalHeadFromPanel(levelXing.getSignalDName());
4213            levelXing.setSignalDName("");
4214        }
4215        //setup logic if requested
4216        if (setupALogic.isSelected() && (aHead != null)) {
4217            setLogicXing(aHead, (TrackSegment) levelXing.getConnectC(),
4218                    levelXing.getLayoutBlockBD(), (TrackSegment) levelXing.getConnectB(),
4219                    (TrackSegment) levelXing.getConnectD(), aSignalHeadComboBox.getSelectedItemDisplayName());
4220        }
4221        if (setupBLogic.isSelected() && (bHead != null)) {
4222            setLogicXing(bHead, (TrackSegment) levelXing.getConnectD(),
4223                    levelXing.getLayoutBlockAC(), (TrackSegment) levelXing.getConnectA(),
4224                    (TrackSegment) levelXing.getConnectC(), bSignalHeadComboBox.getSelectedItemDisplayName());
4225        }
4226        if (setupCLogic.isSelected() && (cHead != null)) {
4227            setLogicXing(cHead, (TrackSegment) levelXing.getConnectA(),
4228                    levelXing.getLayoutBlockBD(), (TrackSegment) levelXing.getConnectB(),
4229                    (TrackSegment) levelXing.getConnectD(), cSignalHeadComboBox.getSelectedItemDisplayName());
4230        }
4231        if (setupDLogic.isSelected() && (dHead != null)) {
4232            setLogicXing(dHead, (TrackSegment) levelXing.getConnectB(),
4233                    levelXing.getLayoutBlockAC(), (TrackSegment) levelXing.getConnectA(),
4234                    (TrackSegment) levelXing.getConnectC(), dSignalHeadComboBox.getSelectedItemDisplayName());
4235        }
4236        //finish up
4237        setSignalsAtLevelXingOpenFlag = false;
4238        setSignalsAtLevelXingFrame.setVisible(false);
4239        if (needRedraw) {
4240            layoutEditor.redrawPanel();
4241            needRedraw = false;
4242            layoutEditor.setDirty();
4243        }
4244    }   //setXingSignalsDonePressed
4245
4246    private boolean getLevelCrossingInformation() {
4247        if (!setSignalsAtLevelXingFromMenuFlag) {
4248            levelXing = null;
4249            List<LevelXing> levelXings = layoutEditor.getLevelXings();
4250            if (levelXings.size() <= 0) {
4251                JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4252                        Bundle.getMessage("SignalsError15"),
4253                        Bundle.getMessage("ErrorTitle"),
4254                        JmriJOptionPane.ERROR_MESSAGE);
4255                return false;
4256            } else if (levelXings.size() == 1) {
4257                levelXing = levelXings.get(0);
4258            } else {
4259                LayoutBlock xingBlockA = null;
4260                xingBlockA = getBlockFromEntry(blockACComboBox);
4261                if (xingBlockA == null) {
4262                    return false;
4263                }
4264
4265                LayoutBlock xingBlockC = getBlockFromEntry(blockBDComboBox);
4266                if (xingBlockC == null) {
4267                    return false;
4268                }
4269
4270                int foundCount = 0;
4271                //make two block tests first
4272                for (LevelXing x : layoutEditor.getLevelXings()) {
4273                    LayoutBlock xA = null;
4274                    LayoutBlock xB = null;
4275                    LayoutBlock xC = null;
4276                    LayoutBlock xD = null;
4277                    LayoutBlock xAC = x.getLayoutBlockAC();
4278                    LayoutBlock xBD = x.getLayoutBlockBD();
4279                    if (x.getConnectA() != null) {
4280                        xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
4281                    }
4282                    if (x.getConnectB() != null) {
4283                        xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
4284                    }
4285                    if (x.getConnectC() != null) {
4286                        xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
4287                    }
4288                    if (x.getConnectD() != null) {
4289                        xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
4290                    }
4291                    if (((xA != null) && (xC != null) && (((xA == xingBlockA) && (xC == xingBlockC))
4292                            || ((xA == xingBlockC) && (xC == xingBlockA))))
4293                            || ((xB != null) && (xD != null) && (((xB == xingBlockA) && (xD == xingBlockC))
4294                            || ((xB == xingBlockC) && (xD == xingBlockA))))) {
4295                        levelXing = x;
4296                        foundCount++;
4297                    } else if ((xAC != null) && (xBD != null) && (((xAC == xingBlockA) && (xBD == xingBlockC))
4298                            || ((xAC == xingBlockC) && (xBD == xingBlockA)))) {
4299                        levelXing = x;
4300                        foundCount++;
4301                    }
4302                }
4303                if (foundCount == 0) {
4304                    //try one block test
4305                    for (LevelXing x : layoutEditor.getLevelXings()) {
4306                        if ((xingBlockA == x.getLayoutBlockAC()) || (xingBlockA == x.getLayoutBlockBD())) {
4307                            levelXing = x;
4308                            foundCount++;
4309                        }
4310                    }
4311                }
4312                if (foundCount > 1) {
4313                    JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4314                            Bundle.getMessage("SignalsError16",
4315                                    new Object[]{" " + foundCount + " "}),
4316                            Bundle.getMessage("ErrorTitle"),
4317                            JmriJOptionPane.ERROR_MESSAGE);
4318                    return false;
4319                }
4320                if (levelXing == null) {
4321                    JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4322                            Bundle.getMessage("SignalsError17"),
4323                            Bundle.getMessage("ErrorTitle"),
4324                            JmriJOptionPane.ERROR_MESSAGE);
4325                    return false;
4326                }
4327            }
4328        }
4329
4330        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4331
4332        Point2D coordsA = levelXingView.getCoordsA();
4333        Point2D coordsC = levelXingView.getCoordsC();
4334        placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsA));
4335
4336        return true;
4337    }   //getLevelCrossingInformation
4338
4339    private boolean getXingSignalHeadInformation() {
4340        //note that all heads are optional, but pairs must be present
4341        aHead = getSignalHeadFromEntry(aSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4342        bHead = getSignalHeadFromEntry(bSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4343        cHead = getSignalHeadFromEntry(cSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4344        dHead = getSignalHeadFromEntry(dSignalHeadComboBox, false, setSignalsAtLevelXingFrame);
4345        if (((aHead != null) && (cHead == null)) || ((aHead == null) && (cHead != null))
4346                || ((bHead != null) && (dHead == null)) || ((bHead == null) && (dHead != null))) {
4347            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4348                    Bundle.getMessage("SignalsError14"),
4349                    Bundle.getMessage("ErrorTitle"),
4350                    JmriJOptionPane.ERROR_MESSAGE);
4351            return false;
4352        }
4353        if ((aHead == null) && (bHead == null) && (cHead == null) && (dHead == null)) {
4354            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4355                    Bundle.getMessage("SignalsError12"),
4356                    Bundle.getMessage("ErrorTitle"),
4357                    JmriJOptionPane.ERROR_MESSAGE);
4358            return false;
4359        }
4360        return true;
4361    }
4362
4363    private void placeXingA() {
4364        if (testIcon == null) {
4365            testIcon = signalIconEditor.getIcon(0);
4366        }
4367        String signalHeadName = aSignalHeadComboBox.getSelectedItemDisplayName();
4368        if (signalHeadName == null) {
4369            signalHeadName = "";
4370        }
4371        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4372
4373        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4374
4375        Point2D coordsA = levelXingView.getCoordsA();
4376        Point2D delta = new Point2D.Double(0.0, +shift);
4377
4378        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
4379        Point2D where = MathUtil.add(coordsA, delta);
4380        setSignalHeadOnPanel(placeSignalDirectionDEG + 180.0, signalHeadName, where);
4381    }
4382
4383    private void placeXingB() {
4384        if (testIcon == null) {
4385            testIcon = signalIconEditor.getIcon(0);
4386        }
4387        String signalHeadName = bSignalHeadComboBox.getSelectedItemDisplayName();
4388        if (signalHeadName == null) {
4389            signalHeadName = "";
4390        }
4391        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4392
4393        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4394
4395        Point2D coordsB = levelXingView.getCoordsB();
4396        Point2D coordsD = levelXingView.getCoordsD();
4397
4398        double directionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsD));
4399        Point2D delta = new Point2D.Double(0.0, -shift);
4400
4401        delta = MathUtil.rotateDEG(delta, directionDEG);
4402        Point2D where = MathUtil.add(coordsB, delta);
4403        setSignalHeadOnPanel(directionDEG, signalHeadName, where);
4404    }
4405
4406    private void placeXingC() {
4407        if (testIcon == null) {
4408            testIcon = signalIconEditor.getIcon(0);
4409        }
4410        String signalHeadName = cSignalHeadComboBox.getSelectedItemDisplayName();
4411        if (signalHeadName == null) {
4412            signalHeadName = "";
4413        }
4414        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4415
4416        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4417
4418        Point2D coordsC = levelXingView.getCoordsC();
4419        Point2D delta = new Point2D.Double(0.0, -shift);
4420
4421        delta = MathUtil.rotateDEG(delta, placeSignalDirectionDEG);
4422        Point2D where = MathUtil.add(coordsC, delta);
4423        setSignalHeadOnPanel(placeSignalDirectionDEG, signalHeadName, where);
4424    }
4425
4426    private void placeXingD() {
4427        if (testIcon == null) {
4428            testIcon = signalIconEditor.getIcon(0);
4429        }
4430        String signalHeadName = dSignalHeadComboBox.getSelectedItemDisplayName();
4431        if (signalHeadName == null) {
4432            signalHeadName = "";
4433        }
4434        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
4435
4436        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
4437
4438        Point2D coordsB = levelXingView.getCoordsB();
4439        Point2D coordsD = levelXingView.getCoordsD();
4440
4441        double directionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsB));
4442        double diffDirDEG = MathUtil.diffAngleDEG(placeSignalDirectionDEG, directionDEG + 180.0);
4443        Point2D delta = new Point2D.Double(-shift * Math.cos(Math.toRadians(diffDirDEG)), -shift);
4444
4445        delta = MathUtil.rotateDEG(delta, directionDEG);
4446        Point2D where = MathUtil.add(coordsD, delta);
4447        setSignalHeadOnPanel(directionDEG, signalHeadName, where);
4448    }
4449
4450    @SuppressWarnings("null")
4451    private void setLogicXing(SignalHead head, TrackSegment track, LayoutBlock crossBlock,
4452            TrackSegment crossTrack1, TrackSegment crossTrack2, String signalHeadName) {
4453        if (track == null) {
4454            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4455                    Bundle.getMessage("InfoMessage7"),
4456                    Bundle.getMessage("MessageTitle"),
4457                    JmriJOptionPane.INFORMATION_MESSAGE);
4458            return;
4459        }
4460        Sensor occupancy = null;
4461        Sensor crossOccupancy = null;
4462        Sensor track1Occupancy = null;
4463        Sensor track2Occupancy = null;
4464        SignalHead nextHead = null;
4465        LayoutBlock block = track.getLayoutBlock();
4466        if (block == null) {
4467            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4468                    Bundle.getMessage("InfoMessage6"),
4469                    Bundle.getMessage("MessageTitle"),
4470                    JmriJOptionPane.INFORMATION_MESSAGE);
4471            return;
4472        }
4473        occupancy = block.getOccupancySensor();
4474        if (occupancy == null) {
4475            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4476                    Bundle.getMessage("InfoMessage4",
4477                            new Object[]{block.getUserName()}),
4478                    Bundle.getMessage("MessageTitle"),
4479                    JmriJOptionPane.INFORMATION_MESSAGE);
4480            return;
4481        }
4482        if (crossBlock != null) {
4483            crossOccupancy = crossBlock.getOccupancySensor();
4484        }
4485        if (crossTrack1 != null) {
4486            block = crossTrack1.getLayoutBlock();
4487            if (block != null) {
4488                track1Occupancy = block.getOccupancySensor();
4489                if (track1Occupancy == crossOccupancy) {
4490                    track1Occupancy = null;
4491                }
4492            }
4493        }
4494        if (crossTrack2 != null) {
4495            block = crossTrack2.getLayoutBlock();
4496            if (block != null) {
4497                track2Occupancy = block.getOccupancySensor();
4498                if ((track2Occupancy == crossOccupancy)
4499                        || (track2Occupancy == track1Occupancy)) {
4500                    track2Occupancy = null;
4501                }
4502            }
4503        }
4504        nextHead = getNextSignalFromObject(track, levelXing,
4505                head.getSystemName(), setSignalsAtXoverTurnoutFrame);
4506        if ((nextHead == null) && (!reachedEndBumper())) {
4507            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4508                    Bundle.getMessage("InfoMessage5",
4509                            new Object[]{block.getUserName()}),
4510                    Bundle.getMessage("MessageTitle"),
4511                    JmriJOptionPane.INFORMATION_MESSAGE);
4512            return;
4513        }
4514        if ((crossOccupancy == null) && (track1Occupancy == null) && (track2Occupancy == null)) {
4515            JmriJOptionPane.showMessageDialog(setSignalsAtLevelXingFrame,
4516                    Bundle.getMessage("SignalsWarn1",
4517                            new Object[]{signalHeadName}),
4518                    Bundle.getMessage("WarningTitle"),
4519                    JmriJOptionPane.WARNING_MESSAGE);
4520        }
4521        if (!initializeBlockBossLogic(head.getDisplayName())) {
4522            return;
4523        }
4524        logic.setMode(BlockBossLogic.SINGLEBLOCK);
4525        logic.setSensor1(occupancy.getDisplayName());
4526        if (nextHead != null) {
4527            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
4528        }
4529        if (auxSignal != null) {
4530            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
4531        }
4532        if (crossOccupancy != null) {
4533            logic.setSensor2(crossOccupancy.getDisplayName());
4534            if (track1Occupancy != null) {
4535                logic.setSensor3(track1Occupancy.getDisplayName());
4536                if (track2Occupancy != null) {
4537                    logic.setSensor4(track2Occupancy.getDisplayName());
4538                }
4539            } else if (track2Occupancy != null) {
4540                logic.setSensor3(track2Occupancy.getDisplayName());
4541            }
4542        } else if (track1Occupancy != null) {
4543            logic.setSensor2(track1Occupancy.getDisplayName());
4544            if (track2Occupancy != null) {
4545                logic.setSensor3(track2Occupancy.getDisplayName());
4546            }
4547        } else if (track2Occupancy != null) {
4548            logic.setSensor2(track2Occupancy.getDisplayName());
4549        }
4550        finalizeBlockBossLogic();
4551    }
4552
4553    /*====================================*\
4554    |* setSignalsAtThroatToThroatTurnouts *|
4555    \*====================================*/
4556    /**
4557     * Tool to set signals at throat-to-throat turnouts, including placing the
4558     * signal icons and setup of signal logic for each signal head
4559     * <p>
4560     * This tool can only be accessed from the Tools menu. There is no access
4561     * from a turnout pop-up menu.
4562     * <p>
4563     * This tool requires a situation where two turnouts are connected throat-
4564     * to-throat by a single "short" track segment. The actual length of the
4565     * track segment is not tested. If this situation is not found, and error
4566     * message is sent to the user. To get started with this the user needs to
4567     * enter at least one of the two connected turnouts.
4568     * <p>
4569     * This tool assumes two turnouts connected throat-to-throat, as would be
4570     * used to represent a double slip turnout. The turnouts may be either
4571     * left-handed, right-handed, wye, or any pair of these. This tool also
4572     * assumes that there are no signals at the throat junction. The signal
4573     * heads will be rotated to face outward--away from the throats. Four sets
4574     * of one or two signal heads will be placed, one at each of the converging
4575     * and diverging for each turnout.
4576     * <p>
4577     * This tool assumes that each of the four tracks is contained in a
4578     * different block. Things work best if the two throat-to-throat turnouts
4579     * are in their own separate block, but this is not necessary.
4580     * <p>
4581     * This tool will place icons on the outside edges of each turnout.
4582     * <p>
4583     * At least one signal at each of the four connection points is required. A
4584     * second signal at each is optional.
4585     */
4586    //operational variables for Set Signals at Double Crossover Turnout tool
4587    private JmriJFrame setSignalsAtThroatToThroatTurnoutsFrame = null;
4588    private boolean setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
4589    private boolean setSignalsAtThroatToThroatTurnoutsFromMenuFlag = false;
4590
4591    private JLabel ttotTurnoutName1Label = null;
4592    private JLabel ttotTurnoutName2Label = null;
4593
4594    private final NamedBeanComboBox<Turnout> turnout1ComboBox = new NamedBeanComboBox<>(
4595            InstanceManager.turnoutManagerInstance(),
4596            null, DisplayOptions.DISPLAYNAME);
4597    private final NamedBeanComboBox<Turnout> turnout2ComboBox = new NamedBeanComboBox<>(
4598            InstanceManager.turnoutManagerInstance(),
4599            null, DisplayOptions.DISPLAYNAME);
4600
4601    private final NamedBeanComboBox<SignalHead> a1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4602            InstanceManager.getDefault(SignalHeadManager.class
4603            ),
4604            null, DisplayOptions.DISPLAYNAME);
4605    private final NamedBeanComboBox<SignalHead> a2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4606            InstanceManager.getDefault(SignalHeadManager.class
4607            ),
4608            null, DisplayOptions.DISPLAYNAME);
4609    private final NamedBeanComboBox<SignalHead> b1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4610            InstanceManager.getDefault(SignalHeadManager.class
4611            ),
4612            null, DisplayOptions.DISPLAYNAME);
4613    private final NamedBeanComboBox<SignalHead> b2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4614            InstanceManager.getDefault(SignalHeadManager.class
4615            ),
4616            null, DisplayOptions.DISPLAYNAME);
4617    private final NamedBeanComboBox<SignalHead> c1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4618            InstanceManager.getDefault(SignalHeadManager.class
4619            ),
4620            null, DisplayOptions.DISPLAYNAME);
4621    private final NamedBeanComboBox<SignalHead> c2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4622            InstanceManager.getDefault(SignalHeadManager.class
4623            ),
4624            null, DisplayOptions.DISPLAYNAME);
4625    private final NamedBeanComboBox<SignalHead> d1TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4626            InstanceManager.getDefault(SignalHeadManager.class
4627            ),
4628            null, DisplayOptions.DISPLAYNAME);
4629    private final NamedBeanComboBox<SignalHead> d2TToTSignalHeadComboBox = new NamedBeanComboBox<>(
4630            InstanceManager.getDefault(SignalHeadManager.class
4631            ),
4632            null, DisplayOptions.DISPLAYNAME);
4633
4634    private final JCheckBox setA1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4635    private final JCheckBox setA2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4636    private final JCheckBox setB1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4637    private final JCheckBox setB2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4638    private final JCheckBox setC1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4639    private final JCheckBox setC2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4640    private final JCheckBox setD1TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4641    private final JCheckBox setD2TToTHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
4642
4643    private final JCheckBox setupA1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4644    private final JCheckBox setupA2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4645    private final JCheckBox setupB1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4646    private final JCheckBox setupB2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4647    private final JCheckBox setupC1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4648    private final JCheckBox setupC2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4649    private final JCheckBox setupD1TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4650    private final JCheckBox setupD2TToTLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
4651
4652    private JButton getSavedTToTSignalHeads = null;
4653    private JButton changeTToTSignalIcon = null;
4654    private JButton setTToTSignalsDone = null;
4655    private JButton setTToTSignalsCancel = null;
4656
4657    private LayoutTurnout layoutTurnout1 = null;
4658    private LayoutTurnout layoutTurnout2 = null;
4659
4660    private Turnout turnout1 = null;
4661    private Turnout turnout2 = null;
4662
4663    private TrackSegment connectorTrack = null;
4664
4665    private String ttotTurnoutName1 = null;
4666    private String ttotTurnoutName2 = null;
4667
4668    private SignalHead a1TToTHead = null;
4669    private SignalHead a2TToTHead = null;
4670    private SignalHead b1TToTHead = null;
4671    private SignalHead b2TToTHead = null;
4672    private SignalHead c1TToTHead = null;
4673    private SignalHead c2TToTHead = null;
4674    private SignalHead d1TToTHead = null;
4675    private SignalHead d2TToTHead = null;
4676
4677    public void setSignalsAtThroatToThroatTurnoutsFromMenu(
4678            @Nonnull LayoutTurnout to, @Nonnull String linkedTurnoutName,
4679            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
4680        ttotTurnoutName1 = to.getTurnoutName();
4681        ttotTurnoutName2 = linkedTurnoutName;
4682
4683        turnout1ComboBox.setSelectedItem(to.getTurnout());
4684        turnout2ComboBox.setSelectedItem(to.getSecondTurnout());
4685
4686        a1TToTSignalHeadComboBox.setSelectedItem(null);
4687        a2TToTSignalHeadComboBox.setSelectedItem(null);
4688        b1TToTSignalHeadComboBox.setSelectedItem(null);
4689        b2TToTSignalHeadComboBox.setSelectedItem(null);
4690        c1TToTSignalHeadComboBox.setSelectedItem(null);
4691        c2TToTSignalHeadComboBox.setSelectedItem(null);
4692        d1TToTSignalHeadComboBox.setSelectedItem(null);
4693        d2TToTSignalHeadComboBox.setSelectedItem(null);
4694
4695        setSignalsAtThroatToThroatTurnoutsFromMenuFlag = true;
4696        setSignalsAtThroatToThroatTurnouts(theEditor, theFrame);
4697        setSignalsAtThroatToThroatTurnoutsFromMenuFlag = false;
4698    }
4699
4700    public void setSignalsAtThroatToThroatTurnouts(
4701            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
4702        signalIconEditor = theEditor;
4703        signalFrame = theFrame;
4704
4705        //Initialize if needed
4706        if (setSignalsAtThroatToThroatTurnoutsFrame == null) {
4707            setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
4708            setSignalsAtThroatToThroatTurnoutsFrame = new JmriJFrame(Bundle.getMessage("SignalsAtTToTTurnout"), false, true);
4709            oneFrameToRuleThemAll(setSignalsAtThroatToThroatTurnoutsFrame);
4710            setSignalsAtThroatToThroatTurnoutsFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
4711            setSignalsAtThroatToThroatTurnoutsFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtTToTTurnout", true);
4712            setSignalsAtThroatToThroatTurnoutsFrame.setLocation(70, 30);
4713            Container theContentPane = setSignalsAtThroatToThroatTurnoutsFrame.getContentPane();
4714            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
4715
4716            JPanel panel1a = new JPanel(new FlowLayout());
4717            ttotTurnoutName1Label = new JLabel(Bundle.getMessage("BeanNameTurnout") + " 1 "
4718                    + Bundle.getMessage("Name"));
4719            panel1a.add(ttotTurnoutName1Label);
4720            panel1a.add(turnout1ComboBox);
4721            turnout1ComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
4722            theContentPane.add(panel1a);
4723
4724            JPanel panel1b = new JPanel(new FlowLayout());
4725            ttotTurnoutName2Label = new JLabel(Bundle.getMessage("BeanNameTurnout") + " 2 "
4726                    + Bundle.getMessage("Name"));
4727            panel1b.add(ttotTurnoutName2Label);
4728            panel1b.add(turnout2ComboBox);
4729            turnout2ComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
4730            theContentPane.add(panel1b);
4731            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4732            //Provide for retrieval of names of previously saved signal heads
4733
4734            JPanel panel20 = new JPanel(new FlowLayout());
4735            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
4736            panel20.add(shTitle);
4737            panel20.add(new JLabel("   "));
4738            panel20.add(getSavedTToTSignalHeads = new JButton(Bundle.getMessage("GetSaved")));
4739            getSavedTToTSignalHeads.addActionListener(this::setSignalsAtTToTTurnoutsGetSaved);
4740            getSavedTToTSignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
4741            theContentPane.add(panel20);
4742            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4743
4744            JPanel panel2a = new JPanel(new FlowLayout());
4745            panel2a.add(new JLabel("   "));
4746            panel2a.add(setPlaceAllHeads);
4747            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
4748            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
4749                boolean isSelected = setPlaceAllHeads.isSelected();
4750                //(de)select all checkboxes
4751                setA1TToTHead.setSelected(isSelected);
4752                setA2TToTHead.setSelected(isSelected);
4753                setB1TToTHead.setSelected(isSelected);
4754                setB2TToTHead.setSelected(isSelected);
4755                setC1TToTHead.setSelected(isSelected);
4756                setC2TToTHead.setSelected(isSelected);
4757                setD1TToTHead.setSelected(isSelected);
4758                setD2TToTHead.setSelected(isSelected);
4759            });
4760            panel2a.add(new JLabel("  "));
4761            panel2a.add(setupAllLogic);
4762            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
4763            setupAllLogic.addActionListener((ActionEvent e) -> {
4764                boolean isSelected = setupAllLogic.isSelected();
4765                //(de)select all checkboxes
4766                setupA1TToTLogic.setSelected(isSelected);
4767                setupA2TToTLogic.setSelected(isSelected);
4768                setupB1TToTLogic.setSelected(isSelected);
4769                setupB2TToTLogic.setSelected(isSelected);
4770                setupC1TToTLogic.setSelected(isSelected);
4771                setupC2TToTLogic.setSelected(isSelected);
4772                setupD1TToTLogic.setSelected(isSelected);
4773                setupD2TToTLogic.setSelected(isSelected);
4774            });
4775            theContentPane.add(panel2a);
4776            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4777
4778            //Signal heads located at turnout 1
4779            JPanel panel20a = new JPanel(new FlowLayout());
4780            panel20a.add(new JLabel(Bundle.getMessage("SignalLocated")
4781                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
4782                    + Bundle.getMessage("ContinuingTrack")));
4783            theContentPane.add(panel20a);
4784
4785            JPanel panel21 = new JPanel(new FlowLayout());
4786            panel21.add(new JLabel(Bundle.getMessage("MakeLabel",
4787                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4788                    + Bundle.getMessage("ContinuingTrack"))));
4789            panel21.add(a1TToTSignalHeadComboBox);
4790            theContentPane.add(panel21);
4791            a1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4792
4793            JPanel panel22 = new JPanel(new FlowLayout());
4794            panel22.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
4795            panel22.add(setA1TToTHead);
4796            setA1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4797            panel22.add(new JLabel("  "));
4798            panel22.add(setupA1TToTLogic);
4799            setupA1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4800            theContentPane.add(panel22);
4801
4802            JPanel panel23 = new JPanel(new FlowLayout());
4803            panel23.add(new JLabel(Bundle.getMessage("MakeLabel",
4804                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4805                    + Bundle.getMessage("DivergingTrack"))));
4806            panel23.add(a2TToTSignalHeadComboBox);
4807            theContentPane.add(panel23);
4808            a2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4809
4810            JPanel panel24 = new JPanel(new FlowLayout());
4811            panel24.add(new JLabel("   "));
4812            panel24.add(setA2TToTHead);
4813            setA2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4814            panel24.add(new JLabel("  "));
4815            panel24.add(setupA2TToTLogic);
4816            setupA2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4817            theContentPane.add(panel24);
4818
4819            JPanel panel31x = new JPanel(new FlowLayout());
4820            panel31x.add(new JLabel(Bundle.getMessage("SignalLocated")
4821                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
4822                    + Bundle.getMessage("DivergingTrack")));
4823            theContentPane.add(panel31x);
4824
4825            JPanel panel31 = new JPanel(new FlowLayout());
4826            panel31.add(new JLabel(Bundle.getMessage("MakeLabel",
4827                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4828                    + Bundle.getMessage("ContinuingTrack"))));
4829            panel31.add(b1TToTSignalHeadComboBox);
4830            theContentPane.add(panel31);
4831            b1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4832
4833            JPanel panel32 = new JPanel(new FlowLayout());
4834            panel32.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
4835            panel32.add(setB1TToTHead);
4836            setB1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4837            panel32.add(new JLabel("  "));
4838            panel32.add(setupB1TToTLogic);
4839            setupB1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4840            theContentPane.add(panel32);
4841
4842            JPanel panel33 = new JPanel(new FlowLayout());
4843            panel33.add(new JLabel(Bundle.getMessage("MakeLabel",
4844                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
4845                    + Bundle.getMessage("DivergingTrack"))));
4846            panel33.add(b2TToTSignalHeadComboBox);
4847            theContentPane.add(panel33);
4848            b2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4849
4850            JPanel panel34 = new JPanel(new FlowLayout());
4851            panel34.add(new JLabel("   "));
4852            panel34.add(setB2TToTHead);
4853            setB2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4854            panel34.add(new JLabel("  "));
4855            panel34.add(setupB2TToTLogic);
4856            setupB2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4857            theContentPane.add(panel34);
4858            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4859            //Signal heads located at turnout 2
4860
4861            JPanel panel41x = new JPanel(new FlowLayout());
4862            panel41x.add(new JLabel(Bundle.getMessage("SignalLocated")
4863                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
4864                    + Bundle.getMessage("ContinuingTrack")));
4865            theContentPane.add(panel41x);
4866
4867            JPanel panel41 = new JPanel(new FlowLayout());
4868            panel33.add(new JLabel(Bundle.getMessage("MakeLabel",
4869                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4870                    + Bundle.getMessage("ContinuingTrack"))));
4871            panel41.add(c1TToTSignalHeadComboBox);
4872            theContentPane.add(panel41);
4873            c1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4874
4875            JPanel panel42 = new JPanel(new FlowLayout());
4876            panel42.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
4877            panel42.add(setC1TToTHead);
4878            setC1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4879            panel42.add(new JLabel("  "));
4880            panel42.add(setupC1TToTLogic);
4881            setupC1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4882            theContentPane.add(panel42);
4883
4884            JPanel panel43 = new JPanel(new FlowLayout());
4885            panel43.add(new JLabel(Bundle.getMessage("MakeLabel",
4886                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4887                    + Bundle.getMessage("DivergingTrack"))));
4888            panel43.add(c2TToTSignalHeadComboBox);
4889            theContentPane.add(panel43);
4890            c2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4891
4892            JPanel panel44 = new JPanel(new FlowLayout());
4893            panel44.add(new JLabel("   "));
4894            panel44.add(setC2TToTHead);
4895            setC2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4896            panel44.add(new JLabel("  "));
4897            panel44.add(setupC2TToTLogic);
4898            setupC2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4899            theContentPane.add(panel44);
4900
4901            JPanel panel51x = new JPanel(new FlowLayout());
4902            panel51x.add(new JLabel(Bundle.getMessage("SignalLocated")
4903                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
4904                    + Bundle.getMessage("DivergingTrack")));
4905            theContentPane.add(panel51x);
4906
4907            JPanel panel51 = new JPanel(new FlowLayout());
4908            panel51.add(new JLabel(Bundle.getMessage("MakeLabel",
4909                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4910                    + Bundle.getMessage("ContinuingTrack"))));
4911            panel51.add(d1TToTSignalHeadComboBox);
4912            theContentPane.add(panel51);
4913            d1TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4914
4915            JPanel panel52 = new JPanel(new FlowLayout());
4916            panel52.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
4917            panel52.add(setD1TToTHead);
4918            setD1TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4919            panel52.add(new JLabel("  "));
4920            panel52.add(setupD1TToTLogic);
4921            setupD1TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4922            theContentPane.add(panel52);
4923
4924            JPanel panel53 = new JPanel(new FlowLayout());
4925            panel53.add(new JLabel(Bundle.getMessage("MakeLabel",
4926                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
4927                    + Bundle.getMessage("DivergingTrack"))));
4928            panel53.add(d2TToTSignalHeadComboBox);
4929            theContentPane.add(panel53);
4930            d2TToTSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
4931
4932            JPanel panel54 = new JPanel(new FlowLayout());
4933            panel54.add(new JLabel("   "));
4934            panel54.add(setD2TToTHead);
4935            setD2TToTHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
4936            panel54.add(new JLabel("  "));
4937            panel54.add(setupD2TToTLogic);
4938            setupD2TToTLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
4939            theContentPane.add(panel54);
4940            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
4941
4942            JPanel panel6 = new JPanel(new FlowLayout());
4943            panel6.add(changeTToTSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
4944            changeTToTSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
4945            changeTToTSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
4946            panel6.add(new JLabel("   "));
4947            panel6.add(setTToTSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
4948            setTToTSignalsDone.addActionListener(this::setTToTSignalsDonePressed);
4949            setTToTSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
4950
4951            panel6.add(setTToTSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
4952            setTToTSignalsCancel.addActionListener(this::setTToTSignalsCancelPressed);
4953            setTToTSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
4954            theContentPane.add(panel6);
4955
4956            //make this button the default button (return or enter activates)
4957            JRootPane rootPane = SwingUtilities.getRootPane(setTToTSignalsDone);
4958            if (rootPane != null) {
4959                rootPane.setDefaultButton(setTToTSignalsDone);
4960            }
4961
4962            setSignalsAtThroatToThroatTurnoutsFrame.addWindowListener(new WindowAdapter() {
4963                @Override
4964                public void windowClosing(WindowEvent e) {
4965                    setTToTSignalsCancelPressed(null);
4966                }
4967            });
4968        }
4969        setPlaceAllHeads.setSelected(false);
4970        setupAllLogic.setSelected(false);
4971
4972        turnout1ComboBox.setVisible(!setSignalsAtThroatToThroatTurnoutsFromMenuFlag);
4973        turnout2ComboBox.setVisible(!setSignalsAtThroatToThroatTurnoutsFromMenuFlag);
4974
4975        if (setSignalsAtThroatToThroatTurnoutsFromMenuFlag) {
4976            ttotTurnoutName1Label.setText(Bundle.getMessage("MakeLabel",
4977                    Bundle.getMessage("BeanNameTurnout") + " 1 "
4978                    + Bundle.getMessage("Name")) + ttotTurnoutName1);
4979            ttotTurnoutName2Label.setText(Bundle.getMessage("MakeLabel",
4980                    Bundle.getMessage("BeanNameTurnout") + " 2 "
4981                    + Bundle.getMessage("Name")) + ttotTurnoutName2);
4982
4983            SwingUtilities.invokeLater(() -> setSignalsAtTToTTurnoutsGetSaved(null));
4984        } else {
4985            ttotTurnoutName1Label.setText(
4986                    Bundle.getMessage("BeanNameTurnout") + " 1 "
4987                    + Bundle.getMessage("Name"));
4988            ttotTurnoutName2Label.setText(
4989                    Bundle.getMessage("BeanNameTurnout") + " 2 "
4990                    + Bundle.getMessage("Name"));
4991        }
4992
4993        if (!setSignalsAtThroatToThroatTurnoutsOpenFlag) {
4994            setSignalsAtThroatToThroatTurnoutsFrame.setPreferredSize(null);
4995            setSignalsAtThroatToThroatTurnoutsFrame.pack();
4996            setSignalsAtThroatToThroatTurnoutsOpenFlag = true;
4997        }
4998        setSignalsAtThroatToThroatTurnoutsFrame.setVisible(true);
4999    }   //setSignalsAtTToTTurnouts
5000
5001    private void setSignalsAtTToTTurnoutsGetSaved(ActionEvent a) {
5002        if (!getTToTTurnoutInformation()) {
5003            return;
5004        }
5005        a1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalB1());
5006        a2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalB2());
5007        b1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalC1());
5008        b2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout1.getSignalC2());
5009        c1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalB1());
5010        c2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalB2());
5011        d1TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalC1());
5012        d2TToTSignalHeadComboBox.setSelectedItem(layoutTurnout2.getSignalC2());
5013    }
5014
5015    private void setTToTSignalsCancelPressed(ActionEvent a) {
5016        setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
5017        setSignalsAtThroatToThroatTurnoutsFrame.setVisible(false);
5018    }
5019
5020    private boolean getTToTTurnoutInformation() {
5021        HitPointType type = HitPointType.NONE;
5022        Object connect = null;
5023
5024        turnout1 = null;
5025        turnout2 = null;
5026
5027        layoutTurnout1 = null;
5028        layoutTurnout2 = null;
5029
5030        if (!setSignalsAtThroatToThroatTurnoutsFromMenuFlag) {
5031            ttotTurnoutName1 = turnout1ComboBox.getSelectedItemDisplayName();
5032            if (ttotTurnoutName1 == null) {
5033                ttotTurnoutName1 = "";
5034            }
5035        }
5036        if (ttotTurnoutName1.isEmpty()) {
5037            //turnout 1 not entered, test turnout 2
5038            ttotTurnoutName2 = turnout2ComboBox.getSelectedItemDisplayName();
5039            if (ttotTurnoutName2 == null) {
5040                ttotTurnoutName2 = "";
5041            }
5042            if (ttotTurnoutName2.isEmpty()) {
5043                //no entries in turnout fields
5044                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5045                        Bundle.getMessage("SignalsError1"),
5046                        Bundle.getMessage("ErrorTitle"),
5047                        JmriJOptionPane.ERROR_MESSAGE);
5048                return false;
5049            }
5050            turnout2 = InstanceManager.turnoutManagerInstance().getTurnout(ttotTurnoutName2);
5051            if (turnout2 == null) {
5052                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5053                        Bundle.getMessage("SignalsError2",
5054                                new Object[]{ttotTurnoutName2}), Bundle.getMessage("ErrorTitle"),
5055                        JmriJOptionPane.ERROR_MESSAGE);
5056                return false;
5057            }
5058            String uname = turnout2.getUserName();
5059            if ((uname == null) || uname.isEmpty()
5060                    || !uname.equals(ttotTurnoutName2)) {
5061                turnout2ComboBox.setSelectedItem(turnout2);
5062            }
5063            layoutTurnout2 = getLayoutTurnoutFromTurnout(turnout2, false, ttotTurnoutName2, setSignalsAtThroatToThroatTurnoutsFrame);
5064            if (layoutTurnout2 == null) {
5065                return false;
5066            }
5067            //have turnout 2 and layout turnout 2 - look for turnout 1
5068            connectorTrack = (TrackSegment) layoutTurnout2.getConnectA();
5069            if (connectorTrack == null) {
5070                //Inform user of error, and terminate
5071                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5072                        Bundle.getMessage("SignalsError18"),
5073                        Bundle.getMessage("ErrorTitle"),
5074                        JmriJOptionPane.ERROR_MESSAGE);
5075                return false;
5076            }
5077            type = connectorTrack.getType1();
5078            connect = connectorTrack.getConnect1();
5079            if (connect == layoutTurnout2) {
5080                type = connectorTrack.getType2();
5081                connect = connectorTrack.getConnect2();
5082            }
5083            if ((type != HitPointType.TURNOUT_A) || (connect == null)) {
5084                //Not two turnouts connected throat-to-throat by a single Track Segment
5085                //Inform user of error and terminate
5086                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5087                        Bundle.getMessage("SignalsError18"),
5088                        Bundle.getMessage("ErrorTitle"),
5089                        JmriJOptionPane.ERROR_MESSAGE);
5090                return false;
5091            }
5092            layoutTurnout1 = (LayoutTurnout) connect;
5093            turnout1 = layoutTurnout1.getTurnout();
5094            if (turnout1 == null) {
5095                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5096                        Bundle.getMessage("SignalsError18"),
5097                        Bundle.getMessage("ErrorTitle"),
5098                        JmriJOptionPane.ERROR_MESSAGE);
5099                return false;
5100            }
5101            turnout1ComboBox.setSelectedItem(turnout1);
5102        } else {
5103            //something was entered in the turnout 1 field
5104            turnout1 = InstanceManager.turnoutManagerInstance().getTurnout(ttotTurnoutName1);
5105            if (turnout1 == null) {
5106                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5107                        Bundle.getMessage("SignalsError2",
5108                                new Object[]{ttotTurnoutName1}), Bundle.getMessage("ErrorTitle"),
5109                        JmriJOptionPane.ERROR_MESSAGE);
5110                return false;
5111            }
5112            String uname = turnout1.getUserName();
5113            if ((uname == null) || uname.isEmpty() || !uname.equals(ttotTurnoutName1)) {
5114                turnout1ComboBox.setSelectedItem(turnout1);
5115            }
5116            //have turnout 1 - get corresponding layoutTurnout
5117            layoutTurnout1 = getLayoutTurnoutFromTurnout(turnout1, false, ttotTurnoutName1, setSignalsAtThroatToThroatTurnoutsFrame);
5118            if (layoutTurnout1 == null) {
5119                return false;
5120            }
5121            turnout1ComboBox.setSelectedItem(layoutTurnout1.getTurnout());
5122            //have turnout 1 and layout turnout 1 - was something entered for turnout 2
5123            ttotTurnoutName2 = turnout2ComboBox.getSelectedItemDisplayName();
5124            if (ttotTurnoutName2 == null) {
5125                ttotTurnoutName2 = "";
5126            }
5127            if (ttotTurnoutName2.isEmpty()) {
5128                //no entry for turnout 2
5129                connectorTrack = (TrackSegment) layoutTurnout1.getConnectA();
5130                if (connectorTrack == null) {
5131                    //Inform user of error, and terminate
5132                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5133                            Bundle.getMessage("SignalsError18"),
5134                            Bundle.getMessage("ErrorTitle"),
5135                            JmriJOptionPane.ERROR_MESSAGE);
5136                    return false;
5137                }
5138                type = connectorTrack.getType1();
5139                connect = connectorTrack.getConnect1();
5140                if (connect == layoutTurnout1) {
5141                    type = connectorTrack.getType2();
5142                    connect = connectorTrack.getConnect2();
5143                }
5144                if ((type != HitPointType.TURNOUT_A) || (connect == null)) {
5145                    //Not two turnouts connected throat-to-throat by a single Track Segment
5146                    //Inform user of error and terminate
5147                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5148                            Bundle.getMessage("SignalsError18"),
5149                            Bundle.getMessage("ErrorTitle"),
5150                            JmriJOptionPane.ERROR_MESSAGE);
5151                    return false;
5152                }
5153                layoutTurnout2 = (LayoutTurnout) connect;
5154                turnout2 = layoutTurnout2.getTurnout();
5155                if (turnout2 == null) {
5156                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5157                            Bundle.getMessage("SignalsError18"),
5158                            Bundle.getMessage("ErrorTitle"),
5159                            JmriJOptionPane.ERROR_MESSAGE);
5160                    return false;
5161                }
5162                turnout2ComboBox.setSelectedItem(turnout2);
5163            } else {
5164                //turnout 2 entered also
5165                turnout2 = InstanceManager.turnoutManagerInstance().getTurnout(ttotTurnoutName2);
5166                if (turnout2 == null) {
5167                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5168                            Bundle.getMessage("SignalsError2",
5169                                    new Object[]{ttotTurnoutName2}), Bundle.getMessage("ErrorTitle"),
5170                            JmriJOptionPane.ERROR_MESSAGE);
5171                    return false;
5172                }
5173                uname = turnout2.getUserName();
5174                if ((uname == null) || uname.isEmpty() || !uname.equals(ttotTurnoutName2)) {
5175                    turnout2ComboBox.setSelectedItem(turnout2);
5176                }
5177                layoutTurnout2 = getLayoutTurnoutFromTurnout(turnout2, false, ttotTurnoutName2, setSignalsAtThroatToThroatTurnoutsFrame);
5178                if (layoutTurnout2 == null) {
5179                    return false;
5180                }
5181                turnout2ComboBox.setSelectedItem(layoutTurnout2.getTurnout());
5182                //check that layout turnout 1 and layout turnout 2 are connected throat-to-throat
5183                if (layoutTurnout1.getConnectA() != layoutTurnout2.getConnectA()) {
5184                    //Not two turnouts connected throat-to-throat by a single Track Segment
5185                    //Inform user of error and terminate
5186                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5187                            Bundle.getMessage("SignalsError18"),
5188                            Bundle.getMessage("ErrorTitle"),
5189                            JmriJOptionPane.ERROR_MESSAGE);
5190                    return false;
5191                }
5192                connectorTrack = (TrackSegment) layoutTurnout1.getConnectA();
5193            }
5194        }
5195        //have both turnouts, correctly connected - complete initialization
5196        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5197        Point2D coordsA = layoutTurnout1View.getCoordsA();
5198        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5199        placeSignalDirectionDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsCenter, coordsA));
5200        return true;
5201    }   //getTToTTurnoutInformation
5202
5203    private void setTToTSignalsDonePressed(ActionEvent a) {
5204        if (!getTToTTurnoutInformation()) {
5205            return;
5206        }
5207        if (!getTToTSignalHeadInformation()) {
5208            return;
5209        }
5210
5211        //place signal icons if requested, and assign signal heads to this turnout
5212        String signalHeadName = a1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5213        if (signalHeadName == null) {
5214            signalHeadName = "";
5215        }
5216        if (setA1TToTHead.isSelected()) {
5217            if (isHeadOnPanel(a1TToTHead)
5218                    && (a1TToTHead != getHeadFromName(layoutTurnout1.getSignalB1Name()))) {
5219                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5220                        Bundle.getMessage("SignalsError6",
5221                                new Object[]{signalHeadName}),
5222                        Bundle.getMessage("ErrorTitle"),
5223                        JmriJOptionPane.ERROR_MESSAGE);
5224                return;
5225            } else {
5226                removeSignalHeadFromPanel(layoutTurnout1.getSignalB1Name());
5227                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5228                    placeA1TToT(signalHeadName);
5229                } else {
5230                    placeB1TToT(signalHeadName);
5231                }
5232                removeAssignment(a1TToTHead);
5233                layoutTurnout1.setSignalB1Name(signalHeadName);
5234                needRedraw = true;
5235            }
5236        } else {
5237            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a1TToTHead, layoutTurnout1);
5238            if (assigned == LayoutTurnout.Geometry.NONE) {
5239                if (isHeadOnPanel(a1TToTHead)
5240                        && isHeadAssignedAnywhere(a1TToTHead)) {
5241                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5242                            Bundle.getMessage("SignalsError8",
5243                                    new Object[]{signalHeadName}),
5244                            Bundle.getMessage("ErrorTitle"),
5245                            JmriJOptionPane.ERROR_MESSAGE);
5246                    return;
5247                } else {
5248                    removeSignalHeadFromPanel(layoutTurnout1.getSignalB1Name());
5249                    removeAssignment(a1TToTHead);
5250                    layoutTurnout1.setSignalB1Name(signalHeadName);
5251                }
5252                //} else if (assigned != B1) {
5253                //need to figure out what to do in this case - assigned to a different position on the same turnout.
5254            }
5255        }
5256
5257        signalHeadName = a2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5258        if (signalHeadName == null) {
5259            signalHeadName = "";
5260        }
5261        if ((a2TToTHead != null) && setA2TToTHead.isSelected()) {
5262            if (isHeadOnPanel(a2TToTHead)
5263                    && (a2TToTHead != getHeadFromName(layoutTurnout1.getSignalB2Name()))) {
5264                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5265                        Bundle.getMessage("SignalsError6",
5266                                new Object[]{signalHeadName}),
5267                        Bundle.getMessage("ErrorTitle"),
5268                        JmriJOptionPane.ERROR_MESSAGE);
5269                return;
5270            } else {
5271                removeSignalHeadFromPanel(layoutTurnout1.getSignalB2Name());
5272                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5273                    placeA2TToT(signalHeadName);
5274                } else {
5275                    placeB2TToT(signalHeadName);
5276                }
5277                removeAssignment(a2TToTHead);
5278                layoutTurnout1.setSignalB2Name(signalHeadName);
5279                needRedraw = true;
5280            }
5281        } else if (a2TToTHead != null) {
5282            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a2TToTHead, layoutTurnout1);
5283            if (assigned == LayoutTurnout.Geometry.NONE) {
5284                if (isHeadOnPanel(a2TToTHead)
5285                        && isHeadAssignedAnywhere(a2TToTHead)) {
5286                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5287                            Bundle.getMessage("SignalsError8",
5288                                    new Object[]{signalHeadName}),
5289                            Bundle.getMessage("ErrorTitle"),
5290                            JmriJOptionPane.ERROR_MESSAGE);
5291                    return;
5292                } else {
5293                    removeSignalHeadFromPanel(layoutTurnout1.getSignalB2Name());
5294                    removeAssignment(a2TToTHead);
5295                    layoutTurnout1.setSignalB2Name(signalHeadName);
5296                }
5297                //} else if (assigned != B2) {
5298                //need to figure out what to do in this case.
5299            }
5300        } else { //a2TToTHead known to be null here
5301            removeSignalHeadFromPanel(layoutTurnout1.getSignalB2Name());
5302            layoutTurnout1.setSignalB2Name("");
5303        }
5304
5305        signalHeadName = b1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5306        if (signalHeadName == null) {
5307            signalHeadName = "";
5308        }
5309        if (setB1TToTHead.isSelected()) {
5310            if (isHeadOnPanel(b1TToTHead)
5311                    && (b1TToTHead != getHeadFromName(layoutTurnout1.getSignalC1Name()))) {
5312                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5313                        Bundle.getMessage("SignalsError6",
5314                                new Object[]{signalHeadName}),
5315                        Bundle.getMessage("ErrorTitle"),
5316                        JmriJOptionPane.ERROR_MESSAGE);
5317                return;
5318            } else {
5319                removeSignalHeadFromPanel(layoutTurnout1.getSignalC1Name());
5320                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5321                    placeB1TToT(signalHeadName);
5322                } else {
5323                    placeA1TToT(signalHeadName);
5324                }
5325                removeAssignment(b1TToTHead);
5326                layoutTurnout1.setSignalC1Name(signalHeadName);
5327                needRedraw = true;
5328            }
5329        } else {
5330            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b1TToTHead, layoutTurnout1);
5331            if (assigned == LayoutTurnout.Geometry.NONE) {
5332                if (isHeadOnPanel(b1TToTHead)
5333                        && isHeadAssignedAnywhere(b1TToTHead)) {
5334                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5335                            Bundle.getMessage("SignalsError8",
5336                                    new Object[]{signalHeadName}),
5337                            Bundle.getMessage("ErrorTitle"),
5338                            JmriJOptionPane.ERROR_MESSAGE);
5339                    return;
5340                } else {
5341                    removeSignalHeadFromPanel(layoutTurnout1.getSignalC1Name());
5342                    removeAssignment(b1TToTHead);
5343                    layoutTurnout1.setSignalC1Name(signalHeadName);
5344                }
5345                //} else if (assigned != C1) {
5346                //need to figure out what to do in this case.
5347            }
5348        }
5349
5350        signalHeadName = b2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5351        if (signalHeadName == null) {
5352            signalHeadName = "";
5353        }
5354        if ((b2TToTHead != null) && setB2TToTHead.isSelected()) {
5355            if (isHeadOnPanel(b2TToTHead)
5356                    && (b2TToTHead != getHeadFromName(layoutTurnout1.getSignalC2Name()))) {
5357                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5358                        Bundle.getMessage("SignalsError6",
5359                                new Object[]{signalHeadName}),
5360                        Bundle.getMessage("ErrorTitle"),
5361                        JmriJOptionPane.ERROR_MESSAGE);
5362                return;
5363            } else {
5364                removeSignalHeadFromPanel(layoutTurnout1.getSignalC2Name());
5365                if (layoutTurnout1.getContinuingSense() == Turnout.CLOSED) {
5366                    placeB2TToT(signalHeadName);
5367                } else {
5368                    placeA2TToT(signalHeadName);
5369                }
5370                removeAssignment(b2TToTHead);
5371                layoutTurnout1.setSignalC2Name(signalHeadName);
5372                needRedraw = true;
5373            }
5374        } else if (b2TToTHead != null) {
5375            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b2TToTHead, layoutTurnout1);
5376            if (assigned == LayoutTurnout.Geometry.NONE) {
5377                if (isHeadOnPanel(b2TToTHead)
5378                        && isHeadAssignedAnywhere(b2TToTHead)) {
5379                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5380                            Bundle.getMessage("SignalsError8",
5381                                    new Object[]{signalHeadName}),
5382                            Bundle.getMessage("ErrorTitle"),
5383                            JmriJOptionPane.ERROR_MESSAGE);
5384                    return;
5385                } else {
5386                    removeSignalHeadFromPanel(layoutTurnout1.getSignalC2Name());
5387                    removeAssignment(b2TToTHead);
5388                    layoutTurnout1.setSignalC2Name(signalHeadName);
5389                }
5390                //} else if (assigned != C2) {
5391                //need to figure out what to do in this case.
5392            }
5393        } else { //b2TToTHead known to be null here
5394            removeSignalHeadFromPanel(layoutTurnout1.getSignalC2Name());
5395            layoutTurnout1.setSignalC2Name("");
5396        }
5397
5398        //signal heads on turnout 2
5399        signalHeadName = c1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5400        if (signalHeadName == null) {
5401            signalHeadName = "";
5402        }
5403        if (setC1TToTHead.isSelected()) {
5404            if (isHeadOnPanel(c1TToTHead)
5405                    && (c1TToTHead != getHeadFromName(layoutTurnout2.getSignalB1Name()))) {
5406                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5407                        Bundle.getMessage("SignalsError6",
5408                                new Object[]{signalHeadName}),
5409                        Bundle.getMessage("ErrorTitle"),
5410                        JmriJOptionPane.ERROR_MESSAGE);
5411                return;
5412            } else {
5413                removeSignalHeadFromPanel(layoutTurnout2.getSignalB1Name());
5414                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5415                    placeC1TToT(signalHeadName);
5416                } else {
5417                    placeD1TToT(signalHeadName);
5418                }
5419                removeAssignment(c1TToTHead);
5420                layoutTurnout2.setSignalB1Name(signalHeadName);
5421                needRedraw = true;
5422            }
5423        } else {
5424            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c1TToTHead, layoutTurnout2);
5425            if (assigned == LayoutTurnout.Geometry.NONE) {
5426                if (isHeadOnPanel(c1TToTHead)
5427                        && isHeadAssignedAnywhere(c1TToTHead)) {
5428                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5429                            Bundle.getMessage("SignalsError8",
5430                                    new Object[]{signalHeadName}),
5431                            Bundle.getMessage("ErrorTitle"),
5432                            JmriJOptionPane.ERROR_MESSAGE);
5433                    return;
5434                } else {
5435                    removeSignalHeadFromPanel(layoutTurnout2.getSignalB1Name());
5436                    removeAssignment(c1TToTHead);
5437                    layoutTurnout2.setSignalB1Name(signalHeadName);
5438                }
5439                //} else if (assigned != B1) {
5440                //need to figure out what to do in this case.
5441            }
5442        }
5443
5444        signalHeadName = c2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5445        if (signalHeadName == null) {
5446            signalHeadName = "";
5447        }
5448        if ((c2TToTHead != null) && setC2TToTHead.isSelected()) {
5449            if (isHeadOnPanel(c2TToTHead)
5450                    && (c2TToTHead != getHeadFromName(layoutTurnout2.getSignalB2Name()))) {
5451                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5452                        Bundle.getMessage("SignalsError6",
5453                                new Object[]{signalHeadName}),
5454                        Bundle.getMessage("ErrorTitle"),
5455                        JmriJOptionPane.ERROR_MESSAGE);
5456                return;
5457            } else {
5458                removeSignalHeadFromPanel(layoutTurnout2.getSignalB2Name());
5459                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5460                    placeC2TToT(signalHeadName);
5461                } else {
5462                    placeD2TToT(signalHeadName);
5463                }
5464                removeAssignment(c2TToTHead);
5465                layoutTurnout2.setSignalB2Name(signalHeadName);
5466                needRedraw = true;
5467            }
5468        } else if (c2TToTHead != null) {
5469            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c2TToTHead, layoutTurnout2);
5470            if (assigned == LayoutTurnout.Geometry.NONE) {
5471                if (isHeadOnPanel(c2TToTHead)
5472                        && isHeadAssignedAnywhere(c2TToTHead)) {
5473                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5474                            Bundle.getMessage("SignalsError8",
5475                                    new Object[]{signalHeadName}),
5476                            Bundle.getMessage("ErrorTitle"),
5477                            JmriJOptionPane.ERROR_MESSAGE);
5478                    return;
5479                } else {
5480                    removeSignalHeadFromPanel(layoutTurnout2.getSignalB2Name());
5481                    removeAssignment(c2TToTHead);
5482                    layoutTurnout2.setSignalB2Name(signalHeadName);
5483                }
5484                //} else if (assigned != B2) {
5485                //need to figure out what to do in this case.
5486            }
5487        } else { //c2TToTHead known to be null here
5488            removeSignalHeadFromPanel(layoutTurnout2.getSignalB2Name());
5489            layoutTurnout2.setSignalB2Name("");
5490        }
5491
5492        signalHeadName = d1TToTSignalHeadComboBox.getSelectedItemDisplayName();
5493        if (signalHeadName == null) {
5494            signalHeadName = "";
5495        }
5496        if (setD1TToTHead.isSelected()) {
5497            if (isHeadOnPanel(d1TToTHead)
5498                    && (d1TToTHead != getHeadFromName(layoutTurnout2.getSignalC1Name()))) {
5499                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5500                        Bundle.getMessage("SignalsError6",
5501                                new Object[]{signalHeadName}),
5502                        Bundle.getMessage("ErrorTitle"),
5503                        JmriJOptionPane.ERROR_MESSAGE);
5504                return;
5505            } else {
5506                removeSignalHeadFromPanel(layoutTurnout2.getSignalC1Name());
5507                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5508                    placeD1TToT(signalHeadName);
5509                } else {
5510                    placeC1TToT(signalHeadName);
5511                }
5512                removeAssignment(d1TToTHead);
5513                layoutTurnout2.setSignalC1Name(signalHeadName);
5514                needRedraw = true;
5515            }
5516        } else {
5517            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d1TToTHead, layoutTurnout2);
5518            if (assigned == LayoutTurnout.Geometry.NONE) {
5519                if (isHeadOnPanel(d1TToTHead)
5520                        && isHeadAssignedAnywhere(d1TToTHead)) {
5521                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5522                            Bundle.getMessage("SignalsError8",
5523                                    new Object[]{signalHeadName}),
5524                            Bundle.getMessage("ErrorTitle"),
5525                            JmriJOptionPane.ERROR_MESSAGE);
5526                    return;
5527                } else {
5528                    removeSignalHeadFromPanel(layoutTurnout2.getSignalC1Name());
5529                    removeAssignment(d1TToTHead);
5530                    layoutTurnout2.setSignalC1Name(signalHeadName);
5531                }
5532                //} else if (assigned != C1) {
5533                //need to figure out what to do in this case.
5534            }
5535        }
5536
5537        signalHeadName = d2TToTSignalHeadComboBox.getSelectedItemDisplayName();
5538        if (signalHeadName == null) {
5539            signalHeadName = "";
5540        }
5541        if ((d2TToTHead != null) && setD2TToTHead.isSelected()) {
5542            if (isHeadOnPanel(d2TToTHead)
5543                    && (d2TToTHead != getHeadFromName(layoutTurnout2.getSignalC2Name()))) {
5544                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5545                        Bundle.getMessage("SignalsError6",
5546                                new Object[]{signalHeadName}),
5547                        Bundle.getMessage("ErrorTitle"),
5548                        JmriJOptionPane.ERROR_MESSAGE);
5549                return;
5550            } else {
5551                removeSignalHeadFromPanel(layoutTurnout2.getSignalC2Name());
5552                if (layoutTurnout2.getContinuingSense() == Turnout.CLOSED) {
5553                    placeD2TToT(signalHeadName);
5554                } else {
5555                    placeC2TToT(signalHeadName);
5556                }
5557                removeAssignment(d2TToTHead);
5558                layoutTurnout2.setSignalC2Name(signalHeadName);
5559                needRedraw = true;
5560            }
5561        } else if (d2TToTHead != null) {
5562            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d2TToTHead, layoutTurnout2);
5563            if (assigned == LayoutTurnout.Geometry.NONE) {
5564                if (isHeadOnPanel(d2TToTHead)
5565                        && isHeadAssignedAnywhere(d2TToTHead)) {
5566                    JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5567                            Bundle.getMessage("SignalsError8",
5568                                    new Object[]{signalHeadName}),
5569                            Bundle.getMessage("ErrorTitle"),
5570                            JmriJOptionPane.ERROR_MESSAGE);
5571                    return;
5572                } else {
5573                    removeSignalHeadFromPanel(layoutTurnout2.getSignalC2Name());
5574                    removeAssignment(d2TToTHead);
5575                    layoutTurnout2.setSignalC2Name(signalHeadName);
5576                }
5577                //} else if (assigned != C2) {
5578                //need to figure out what to do in this case.
5579            }
5580        } else { //d2TToTHead known to be null here
5581            removeSignalHeadFromPanel(layoutTurnout2.getSignalC2Name());
5582            layoutTurnout2.setSignalC2Name("");
5583        }
5584
5585        //setup logic if requested
5586        if (setupA1TToTLogic.isSelected() || setupA2TToTLogic.isSelected()) {
5587            setLogicTToT(a1TToTHead, (TrackSegment) layoutTurnout2.getConnectB(), a2TToTHead,
5588                    (TrackSegment) layoutTurnout2.getConnectC(), setupA1TToTLogic.isSelected(),
5589                    setupA2TToTLogic.isSelected(), true, layoutTurnout2, layoutTurnout1);
5590        }
5591        if (setupB1TToTLogic.isSelected() || setupB2TToTLogic.isSelected()) {
5592            setLogicTToT(b1TToTHead, (TrackSegment) layoutTurnout2.getConnectB(), b2TToTHead,
5593                    (TrackSegment) layoutTurnout2.getConnectC(), setupB1TToTLogic.isSelected(),
5594                    setupB2TToTLogic.isSelected(), false, layoutTurnout2, layoutTurnout1);
5595        }
5596        if (setupC1TToTLogic.isSelected() || setupC2TToTLogic.isSelected()) {
5597            setLogicTToT(c1TToTHead, (TrackSegment) layoutTurnout1.getConnectB(), c2TToTHead,
5598                    (TrackSegment) layoutTurnout1.getConnectC(), setupC1TToTLogic.isSelected(),
5599                    setupC2TToTLogic.isSelected(), true, layoutTurnout1, layoutTurnout2);
5600        }
5601        if (setupD1TToTLogic.isSelected() || setupD2TToTLogic.isSelected()) {
5602            setLogicTToT(d1TToTHead, (TrackSegment) layoutTurnout1.getConnectB(), d2TToTHead,
5603                    (TrackSegment) layoutTurnout1.getConnectC(), setupD1TToTLogic.isSelected(),
5604                    setupD2TToTLogic.isSelected(), false, layoutTurnout1, layoutTurnout2);
5605        }
5606        //link the two turnouts
5607        layoutTurnout1.setLinkedTurnoutName(turnout2ComboBox.getSelectedItemDisplayName());
5608        layoutTurnout1.setLinkType(LayoutTurnout.LinkType.THROAT_TO_THROAT);
5609        layoutTurnout2.setLinkedTurnoutName(turnout1ComboBox.getSelectedItemDisplayName());
5610        layoutTurnout2.setLinkType(LayoutTurnout.LinkType.THROAT_TO_THROAT);
5611        //finish up
5612        setSignalsAtThroatToThroatTurnoutsOpenFlag = false;
5613        setSignalsAtThroatToThroatTurnoutsFrame.setVisible(false);
5614        if (needRedraw) {
5615            layoutEditor.redrawPanel();
5616            needRedraw = false;
5617            layoutEditor.setDirty();
5618        }
5619    }   //setTToTSignalsDonePressed
5620
5621    private boolean getTToTSignalHeadInformation() {
5622        a1TToTHead = getSignalHeadFromEntry(a1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5623        if (a1TToTHead == null) {
5624            return false;
5625        }
5626        a2TToTHead = getSignalHeadFromEntry(a2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5627        b1TToTHead = getSignalHeadFromEntry(b1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5628        if (b1TToTHead == null) {
5629            return false;
5630        }
5631        b2TToTHead = getSignalHeadFromEntry(b2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5632        c1TToTHead = getSignalHeadFromEntry(c1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5633        if (c1TToTHead == null) {
5634            return false;
5635        }
5636        c2TToTHead = getSignalHeadFromEntry(c2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5637        d1TToTHead = getSignalHeadFromEntry(d1TToTSignalHeadComboBox, true, setSignalsAtThroatToThroatTurnoutsFrame);
5638        if (d1TToTHead == null) {
5639            return false;
5640        }
5641        d2TToTHead = getSignalHeadFromEntry(d2TToTSignalHeadComboBox, false, setSignalsAtThroatToThroatTurnoutsFrame);
5642        return true;
5643    }
5644
5645    private void placeA1TToT(String signalHeadName) {
5646        //place head near the continuing track of turnout 1
5647        if (testIcon == null) {
5648            testIcon = signalIconEditor.getIcon(0);
5649        }
5650        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5651
5652        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5653        Point2D coordsB = layoutTurnout1View.getCoordsB();
5654        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5655
5656        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5657        Point2D delta = new Point2D.Double(0.0, -shift);
5658
5659        delta = MathUtil.rotateDEG(delta, bDirDEG);
5660        Point2D where = MathUtil.add(coordsB, delta);
5661        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5662    }
5663
5664    private void placeA2TToT(String signalHeadName) {
5665        if (testIcon == null) {
5666            testIcon = signalIconEditor.getIcon(0);
5667        }
5668        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5669
5670        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5671        Point2D coordsB = layoutTurnout1View.getCoordsB();
5672        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5673
5674        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5675        Point2D delta = new Point2D.Double(2.0 * shift, -shift);
5676
5677        delta = MathUtil.rotateDEG(delta, bDirDEG);
5678        Point2D where = MathUtil.add(coordsB, delta);
5679        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5680    }
5681
5682    private void placeB1TToT(String signalHeadName) {
5683        if (testIcon == null) {
5684            testIcon = signalIconEditor.getIcon(0);
5685        }
5686        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5687
5688        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5689        Point2D coordsB = layoutTurnout1View.getCoordsB();
5690        Point2D coordsC = layoutTurnout1View.getCoordsC();
5691        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5692
5693        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5694        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5695        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5696        double shiftX = 0.0;
5697        if (diffDirDEG >= 0.0) {
5698            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5699        }
5700        Point2D delta = new Point2D.Double(shiftX, -shift);
5701
5702        delta = MathUtil.rotateDEG(delta, cDirDEG);
5703        Point2D where = MathUtil.add(coordsC, delta);
5704        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5705    }
5706
5707    private void placeB2TToT(String signalHeadName) {
5708        if (testIcon == null) {
5709            testIcon = signalIconEditor.getIcon(0);
5710        }
5711        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5712
5713        LayoutTurnoutView layoutTurnout1View = layoutEditor.getLayoutTurnoutView(layoutTurnout1);
5714        Point2D coordsB = layoutTurnout1View.getCoordsB();
5715        Point2D coordsC = layoutTurnout1View.getCoordsC();
5716        Point2D coordsCenter = layoutTurnout1View.getCoordsCenter();
5717
5718        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5719        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5720        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5721        double shiftX = 2.0 * shift;
5722        if (diffDirDEG >= 0.0) {
5723            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5724        }
5725        Point2D delta = new Point2D.Double(shiftX, -shift);
5726
5727        delta = MathUtil.rotateDEG(delta, cDirDEG);
5728        Point2D where = MathUtil.add(coordsC, delta);
5729        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5730    }
5731
5732    private void placeC1TToT(String signalHeadName) {
5733        if (testIcon == null) {
5734            testIcon = signalIconEditor.getIcon(0);
5735        }
5736        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5737
5738        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5739        Point2D coordsB = layoutTurnout2View.getCoordsB();
5740        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5741
5742        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5743        Point2D delta = new Point2D.Double(0.0, -shift);
5744
5745        delta = MathUtil.rotateDEG(delta, bDirDEG);
5746        Point2D where = MathUtil.add(coordsB, delta);
5747        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5748    }
5749
5750    private void placeC2TToT(String signalHeadName) {
5751        if (testIcon == null) {
5752            testIcon = signalIconEditor.getIcon(0);
5753        }
5754        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5755
5756        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5757        Point2D coordsB = layoutTurnout2View.getCoordsB();
5758        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5759
5760        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5761        Point2D delta = new Point2D.Double(2.0 * shift, -shift);
5762
5763        delta = MathUtil.rotateDEG(delta, bDirDEG);
5764        Point2D where = MathUtil.add(coordsB, delta);
5765        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
5766    }
5767
5768    private void placeD1TToT(String signalHeadName) {
5769        if (testIcon == null) {
5770            testIcon = signalIconEditor.getIcon(0);
5771        }
5772        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5773
5774        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5775        Point2D coordsB = layoutTurnout2View.getCoordsB();
5776        Point2D coordsC = layoutTurnout2View.getCoordsC();
5777        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5778
5779        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5780        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5781        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5782        double shiftX = 0.0;
5783        if (diffDirDEG >= 0.0) {
5784            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5785        }
5786        Point2D delta = new Point2D.Double(shiftX, -shift);
5787
5788        delta = MathUtil.rotateDEG(delta, cDirDEG);
5789        Point2D where = MathUtil.add(coordsC, delta);
5790        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5791    }
5792
5793    private void placeD2TToT(String signalHeadName) {
5794        if (testIcon == null) {
5795            testIcon = signalIconEditor.getIcon(0);
5796        }
5797        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
5798
5799        LayoutTurnoutView layoutTurnout2View = layoutEditor.getLayoutTurnoutView(layoutTurnout2);
5800        Point2D coordsB = layoutTurnout2View.getCoordsB();
5801        Point2D coordsC = layoutTurnout2View.getCoordsC();
5802        Point2D coordsCenter = layoutTurnout2View.getCoordsCenter();
5803
5804        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
5805        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
5806        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
5807        double shiftX = 2.0 * shift;
5808        if (diffDirDEG >= 0.0) {
5809            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
5810        }
5811        Point2D delta = new Point2D.Double(shiftX, -shift);
5812
5813        delta = MathUtil.rotateDEG(delta, cDirDEG);
5814        Point2D where = MathUtil.add(coordsC, delta);
5815        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
5816    }
5817
5818    @SuppressWarnings("null")
5819    private void setLogicTToT(SignalHead head, TrackSegment track1, SignalHead secondHead, TrackSegment track2,
5820            boolean setup1, boolean setup2, boolean continuing,
5821            LayoutTurnout farTurnout, LayoutTurnout nearTurnout) {
5822        //initialize common components and ensure all is defined
5823        LayoutBlock connectorBlock = connectorTrack.getLayoutBlock();
5824        LayoutBlock nearTurnoutBlock = nearTurnout.getLayoutBlock();
5825        LayoutBlock farTurnoutBlock = farTurnout.getLayoutBlock();
5826        Sensor connectorOccupancy = null;
5827        if ((connectorBlock == null) || (nearTurnoutBlock == null) || (farTurnoutBlock == null)) {
5828            JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5829                    Bundle.getMessage("InfoMessage6"),
5830                    Bundle.getMessage("MessageTitle"),
5831                    JmriJOptionPane.INFORMATION_MESSAGE);
5832            return;
5833        }
5834        connectorOccupancy = connectorBlock.getOccupancySensor();
5835        if (connectorOccupancy == null) {
5836            JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5837                    Bundle.getMessage("InfoMessage4",
5838                            new Object[]{connectorBlock.getUserName()}),
5839                    Bundle.getMessage("MessageTitle"),
5840                    JmriJOptionPane.INFORMATION_MESSAGE);
5841            return;
5842        }
5843        //setup signal head for continuing track of far turnout (or both tracks of far turnout)
5844        if ((track1 == null) && setup1) {
5845            JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5846                    Bundle.getMessage("InfoMessage7"),
5847                    Bundle.getMessage("MessageTitle"),
5848                    JmriJOptionPane.INFORMATION_MESSAGE);
5849            return;
5850        }
5851        Sensor occupancy = null;
5852        SignalHead nextHead = null;
5853        if ((track1 != null) && setup1) {
5854            LayoutBlock block = track1.getLayoutBlock();
5855            if (block == null) {
5856                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5857                        Bundle.getMessage("InfoMessage6"),
5858                        Bundle.getMessage("MessageTitle"),
5859                        JmriJOptionPane.INFORMATION_MESSAGE);
5860                return;
5861            }
5862            occupancy = block.getOccupancySensor();
5863            if (occupancy == null) {
5864                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5865                        Bundle.getMessage("InfoMessage4",
5866                                new Object[]{block.getUserName()}),
5867                        Bundle.getMessage("MessageTitle"),
5868                        JmriJOptionPane.INFORMATION_MESSAGE);
5869                return;
5870            }
5871            nextHead = getNextSignalFromObject(track1, farTurnout,
5872                    head.getSystemName(), setSignalsAtThroatToThroatTurnoutsFrame);
5873            if ((nextHead == null) && (!reachedEndBumper())) {
5874                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
5875                        Bundle.getMessage("InfoMessage5",
5876                                new Object[]{block.getUserName()}),
5877                        Bundle.getMessage("MessageTitle"),
5878                        JmriJOptionPane.INFORMATION_MESSAGE);
5879                return;
5880            }
5881            if (secondHead != null) {
5882                //this head signals only the continuing track of the far turnout
5883                if (!initializeBlockBossLogic(head.getDisplayName())) {
5884                    return;
5885                }
5886                logic.setMode(BlockBossLogic.TRAILINGMAIN);
5887                logic.setTurnout(farTurnout.getTurnout().getDisplayName());
5888                logic.setSensor1(occupancy.getDisplayName());
5889                if (occupancy != connectorOccupancy) {
5890                    logic.setSensor2(connectorOccupancy.getDisplayName());
5891                }
5892                if (nextHead != null) {
5893                    logic.setWatchedSignal1(nextHead.getDisplayName(), false);
5894                }
5895                if (auxSignal != null) {
5896                    logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
5897                }
5898                String nearSensorName = setupNearLogix(nearTurnout, continuing, head);
5899                addNearSensorToLogic(nearSensorName);
5900                finalizeBlockBossLogic();
5901            }
5902        }
5903        if ((secondHead != null) && !setup2) {
5904            return;
5905        }
5906        SignalHead savedAuxSignal = auxSignal;
5907        if (track2 == null) {
5908            JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5909                    Bundle.getMessage("InfoMessage7"),
5910                    Bundle.getMessage("MessageTitle"),
5911                    JmriJOptionPane.INFORMATION_MESSAGE);
5912            return;
5913        }
5914        LayoutBlock block2 = track2.getLayoutBlock();
5915        if (block2 == null) {
5916            JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5917                    Bundle.getMessage("InfoMessage6"),
5918                    Bundle.getMessage("MessageTitle"),
5919                    JmriJOptionPane.INFORMATION_MESSAGE);
5920            return;
5921        }
5922        Sensor occupancy2 = block2.getOccupancySensor();
5923        if (occupancy2 == null) {
5924            JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5925                    Bundle.getMessage("InfoMessage4",
5926                            new Object[]{block2.getUserName()}),
5927                    Bundle.getMessage("MessageTitle"),
5928                    JmriJOptionPane.INFORMATION_MESSAGE);
5929            return;
5930        }
5931        SignalHead nextHead2 = null;
5932        if (secondHead != null) {
5933            nextHead2 = getNextSignalFromObject(track2,
5934                    farTurnout, secondHead.getSystemName(), setSignalsAtThroatToThroatTurnoutsFrame);
5935            if ((nextHead2 == null) && (!reachedEndBumper())) {
5936                JmriJOptionPane.showMessageDialog(setSignalsAtThroatToThroatTurnoutsFrame,
5937                        Bundle.getMessage("InfoMessage5",
5938                                new Object[]{block2.getUserName()}),
5939                        Bundle.getMessage("MessageTitle"),
5940                        JmriJOptionPane.INFORMATION_MESSAGE);
5941                return;
5942            }
5943        }
5944        if ((secondHead == null) && (track1 != null) && setup1) {
5945            if (!initializeBlockBossLogic(head.getDisplayName())) {
5946                return;
5947            }
5948            logic.setMode(BlockBossLogic.FACING);
5949            logic.setTurnout(farTurnout.getTurnout().getDisplayName());
5950            logic.setWatchedSensor1(occupancy.getDisplayName());
5951            logic.setWatchedSensor2(occupancy2.getDisplayName());
5952            logic.setSensor2(connectorOccupancy.getDisplayName());
5953            if (nextHead != null) {
5954                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
5955            }
5956            if (savedAuxSignal != null) {
5957                logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
5958            }
5959            if (nextHead2 != null) {
5960                logic.setWatchedSignal2(nextHead2.getDisplayName());
5961            }
5962            if (auxSignal != null) {
5963                logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
5964            }
5965            String nearSensorName = setupNearLogix(nearTurnout, continuing, head);
5966            addNearSensorToLogic(nearSensorName);
5967            logic.setLimitSpeed2(true);
5968            finalizeBlockBossLogic();
5969        } else if ((secondHead != null) && setup2) {
5970            if (!initializeBlockBossLogic(secondHead.getDisplayName())) {
5971                return;
5972            }
5973            logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
5974            logic.setTurnout(farTurnout.getTurnout().getDisplayName());
5975            logic.setSensor1(occupancy2.getDisplayName());
5976            if (occupancy2 != connectorOccupancy) {
5977                logic.setSensor2(connectorOccupancy.getDisplayName());
5978            }
5979            if (nextHead2 != null) {
5980                logic.setWatchedSignal1(nextHead2.getDisplayName(), false);
5981            }
5982            if (auxSignal != null) {
5983                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
5984            }
5985            String nearSensorName = setupNearLogix(nearTurnout, continuing, head);
5986            addNearSensorToLogic(nearSensorName);
5987            logic.setLimitSpeed2(true);
5988            finalizeBlockBossLogic();
5989        }
5990    }   //setLogicTToT
5991
5992    /*
5993     * Sets up a Logix to set a sensor active if a turnout is set against
5994     *  a track.  This routine creates an internal sensor for the purpose.
5995     * Note: The sensor and logix are named pref + S or pref + X followed by TTT_X_HHH where
5996     *  TTT is the system name of the turnout, X is either C or T depending
5997     *  on "continuing", and HHH is the system name of the signal head.
5998     * Note: If there is any problem, a string of "" is returned, and a warning
5999     *  message is issued.
6000     */
6001    private String setupNearLogix(LayoutTurnout nearTurnout, boolean continuing,
6002            SignalHead head) {
6003        String turnoutName = nearTurnout.getTurnout().getSystemName();
6004        String namer = turnoutName + "_T_" + head.getSystemName();
6005        if (!continuing) {
6006            namer = turnoutName + "_C_" + head.getSystemName();
6007        }
6008        String pref = InstanceManager.getDefault(jmri.LogixManager.class).getSystemPrefix();
6009        String sensorName = pref + "S" + namer;
6010        String logixName = pref + "X" + namer;
6011        try {
6012            InstanceManager.sensorManagerInstance().provideSensor(sensorName);
6013        } catch (IllegalArgumentException ex) {
6014            log.error("Trouble creating sensor {} while setting up Logix.", sensorName);
6015            return "";
6016
6017        }
6018        if (InstanceManager.getDefault(LogixManager.class).getBySystemName(logixName) == null) {
6019            //Logix does not exist, create it
6020            Logix x = InstanceManager.getDefault(LogixManager.class).createNewLogix(logixName, "");
6021            if (x == null) {
6022                log.error("Trouble creating logix {} while setting up signal logic.", logixName);
6023                return "";
6024            }
6025            String cName = x.getSystemName() + "C1";
6026            Conditional c = InstanceManager.getDefault(ConditionalManager.class).
6027                    createNewConditional(cName, "");
6028            if (c == null) {
6029                log.error("Trouble creating conditional {} while setting up Logix.", cName);
6030                return "";
6031            }
6032            Conditional.Type type = Conditional.Type.TURNOUT_THROWN;
6033            if (!continuing) {
6034                type = Conditional.Type.TURNOUT_CLOSED;
6035            }
6036            List<ConditionalVariable> variableList = c.getCopyOfStateVariables();
6037            variableList.add(new ConditionalVariable(false, Conditional.Operator.AND,
6038                    type, turnoutName, true));
6039            c.setStateVariables(variableList);
6040            List<ConditionalAction> actionList = c.getCopyOfActions();
6041            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
6042                    Conditional.Action.SET_SENSOR, sensorName,
6043                    Sensor.ACTIVE, ""));
6044            actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE,
6045                    Conditional.Action.SET_SENSOR, sensorName,
6046                    Sensor.INACTIVE, ""));
6047            c.setAction(actionList); // string data
6048            x.addConditional(cName, -1);
6049            x.activateLogix();
6050        }
6051        return sensorName;
6052    }
6053
6054    /*
6055     * Adds the sensor specified to the open BlockBossLogic, provided it is not already there and
6056     *  provided there is an open slot. If 'name' is null or empty, returns without doing anything.
6057     */
6058    private void addNearSensorToLogic(String name) {
6059        if ((name != null) && !name.isEmpty()) {
6060            //return if a sensor by this name is already present
6061            if ((logic.getSensor1() != null) && (logic.getSensor1().equals(name))) {
6062                return;
6063            }
6064            if ((logic.getSensor2() != null) && (logic.getSensor2().equals(name))) {
6065                return;
6066            }
6067            if ((logic.getSensor3() != null) && (logic.getSensor3().equals(name))) {
6068                return;
6069            }
6070            if ((logic.getSensor4() != null) && (logic.getSensor4().equals(name))) {
6071                return;
6072            }
6073            if ((logic.getSensor5() != null) && (logic.getSensor5().equals(name))) {
6074                return;
6075            }
6076            //add in the first available slot
6077            if (logic.getSensor1() == null) {
6078                logic.setSensor1(name);
6079            } else if (logic.getSensor2() == null) {
6080                logic.setSensor2(name);
6081            } else if (logic.getSensor3() == null) {
6082                logic.setSensor3(name);
6083            } else if (logic.getSensor4() == null) {
6084                logic.setSensor4(name);
6085            } else if (logic.getSensor5() == null) {
6086                logic.setSensor5(name);
6087            } else {
6088                log.error("Error - could not add sensor to SSL for signal head {}", logic.getDrivenSignal());
6089            }
6090        }
6091    }
6092
6093    /*=========================*\
6094    |* setSignalsAt3WayTurnout *|
6095    \*=========================*/
6096    /**
6097     * Tool to set signals at a three-way turnout, including placing the signal
6098     * icons and setup of signal logic for each signal head
6099     * <p>
6100     * This tool can only be accessed from the Tools menu. There is no access
6101     * from a turnout pop-up menu.
6102     * <p>
6103     * This tool requires a situation where two turnouts are connected to model
6104     * a 3-way turnout, with the throat of the second turnout connected to the
6105     * continuing leg of the first turnout by a very short track segment. The
6106     * actual length of the track segment is not tested. If this situation is
6107     * not found, and error message is sent to the user.
6108     * <p>
6109     * This tool assumes two turnouts connected with the throat of the second
6110     * turnout connected to the continuing leg of the first turnou, as used to
6111     * represent a 3-way turnout. The turnouts may be either left-handed, or
6112     * right-handed, or any pair of these. This tool also assumes that there are
6113     * no signals between the two turnouts. Signal heads are allowed/required at
6114     * the continuing leg of the second turnout, at each of the diverging legs,
6115     * and at the throat. At the throat, either one or three heads are provided
6116     * for. So four or six heads will be placed.
6117     * <p>
6118     * This tool assumes that each of the four tracks, the continuing, the two
6119     * diverging, and the throat is contained in a different block. The two
6120     * turnouts used to model the 3-way turnout must be in the same block.
6121     * Things work best if the two turnouts are in the same block as the track
6122     * connecting at the throat, or if the two turnouts are in their own
6123     * separate block, either works fine.
6124     */
6125    //operational variables for Set Signals at 3-Way Turnout tool
6126    private JmriJFrame setSignalsAt3WayTurnoutFrame = null;
6127    private boolean setSignalsAt3WayTurnoutOpenFlag = false;
6128    private boolean setSignalsAt3WayTurnoutFromMenuFlag = false;
6129
6130    private JLabel turnoutANameLabel = null;
6131    private JLabel turnoutBNameLabel = null;
6132
6133    private final NamedBeanComboBox<Turnout> turnoutAComboBox = new NamedBeanComboBox<>(
6134            InstanceManager.turnoutManagerInstance(),
6135            null, DisplayOptions.DISPLAYNAME);
6136    private final NamedBeanComboBox<Turnout> turnoutBComboBox = new NamedBeanComboBox<>(
6137            InstanceManager.turnoutManagerInstance(),
6138            null, DisplayOptions.DISPLAYNAME);
6139
6140    private final NamedBeanComboBox<SignalHead> a1_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6141            InstanceManager.getDefault(SignalHeadManager.class
6142            ),
6143            null, DisplayOptions.DISPLAYNAME);
6144    private final NamedBeanComboBox<SignalHead> a2_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6145            InstanceManager.getDefault(SignalHeadManager.class
6146            ),
6147            null, DisplayOptions.DISPLAYNAME);
6148    private final NamedBeanComboBox<SignalHead> a3_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6149            InstanceManager.getDefault(SignalHeadManager.class
6150            ),
6151            null, DisplayOptions.DISPLAYNAME);
6152    private final NamedBeanComboBox<SignalHead> b_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6153            InstanceManager.getDefault(SignalHeadManager.class
6154            ),
6155            null, DisplayOptions.DISPLAYNAME);
6156    private final NamedBeanComboBox<SignalHead> c_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6157            InstanceManager.getDefault(SignalHeadManager.class
6158            ),
6159            null, DisplayOptions.DISPLAYNAME);
6160    private final NamedBeanComboBox<SignalHead> d_3WaySignalHeadComboBox = new NamedBeanComboBox<>(
6161            InstanceManager.getDefault(SignalHeadManager.class
6162            ),
6163            null, DisplayOptions.DISPLAYNAME);
6164
6165    private final JCheckBox setA13WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6166    private final JCheckBox setupA13WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6167    private final JCheckBox setA23WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6168    private final JCheckBox setupA23WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6169    private final JCheckBox setA33WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6170    private final JCheckBox setupA33WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6171    private final JCheckBox setB3WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6172    private final JCheckBox setupB3WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6173    private final JCheckBox setC3WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6174    private final JCheckBox setupC3WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6175    private final JCheckBox setD3WayHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
6176    private final JCheckBox setupD3WayLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
6177    private JButton getSaved3WaySignalHeads = null;
6178    private JButton change3WaySignalIcon = null;
6179    private JButton set3WaySignalsDone = null;
6180    private JButton set3WaySignalsCancel = null;
6181    private LayoutTurnout layoutTurnoutA = null;
6182    private LayoutTurnout layoutTurnoutB = null;
6183    private Turnout turnoutA = null;
6184    private Turnout turnoutB = null;
6185    //private TrackSegment conTrack = null;
6186    private SignalHead a13WayHead = null; //saved in A1 of Turnout A - Throat - continuing
6187    private SignalHead a23WayHead = null; //saved in A2 of Turnout A - Throat - diverging A (optional)
6188    private SignalHead a33WayHead = null; //saved in A3 of Turnout A - Throat - diverging B (optional)
6189    private SignalHead b3WayHead = null;  //saved in C1 of Turnout A - at diverging A
6190    private SignalHead c3WayHead = null;  //saved in B1 of Turnout B - at continuing
6191    private SignalHead d3WayHead = null;  //saved in C1 of Turnout B - at diverging B
6192
6193    public void setSignalsAt3WayTurnoutFromMenu(
6194            @Nonnull String aName, @Nonnull String bName,
6195            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
6196        Turnout ta = InstanceManager.getDefault(jmri.TurnoutManager.class).getTurnout(aName);
6197        Turnout tb = InstanceManager.getDefault(jmri.TurnoutManager.class).getTurnout(bName);
6198        turnoutAComboBox.setSelectedItem(ta);
6199        turnoutBComboBox.setSelectedItem(tb);
6200        a1_3WaySignalHeadComboBox.setSelectedItem(null);
6201        a2_3WaySignalHeadComboBox.setSelectedItem(null);
6202        a3_3WaySignalHeadComboBox.setSelectedItem(null);
6203        b_3WaySignalHeadComboBox.setSelectedItem(null);
6204        c_3WaySignalHeadComboBox.setSelectedItem(null);
6205        d_3WaySignalHeadComboBox.setSelectedItem(null);
6206        setSignalsAt3WayTurnoutFromMenuFlag = true;
6207        setSignalsAt3WayTurnout(theEditor, theFrame);
6208        setSignalsAt3WayTurnoutFromMenuFlag = false;
6209    }
6210
6211    public void setSignalsAt3WayTurnout(@Nonnull MultiIconEditor theEditor,
6212            @Nonnull JFrame theFrame) {
6213        signalIconEditor = theEditor;
6214        signalFrame = theFrame;
6215
6216        //Initialize if needed
6217        if (setSignalsAt3WayTurnoutFrame == null) {
6218            setSignalsAt3WayTurnoutOpenFlag = false;
6219            setSignalsAt3WayTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalsAt3WayTurnout"), false, true);
6220            oneFrameToRuleThemAll(setSignalsAt3WayTurnoutFrame);
6221            setSignalsAt3WayTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
6222            setSignalsAt3WayTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAt3WayTurnout", true);
6223            setSignalsAt3WayTurnoutFrame.setLocation(70, 30);
6224            Container theContentPane = setSignalsAt3WayTurnoutFrame.getContentPane();
6225            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
6226
6227            JPanel panel1A = new JPanel(new FlowLayout());
6228            turnoutANameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutAName")));
6229            panel1A.add(turnoutANameLabel);
6230            panel1A.add(turnoutAComboBox);
6231            turnoutAComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
6232            theContentPane.add(panel1A);
6233
6234            JPanel panel1B = new JPanel(new FlowLayout());
6235            turnoutBNameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("TurnoutBName")));
6236            panel1B.add(turnoutBNameLabel);
6237            panel1B.add(turnoutBComboBox);
6238            turnoutBComboBox.setToolTipText(Bundle.getMessage("SignalsTurnoutNameHint"));
6239            theContentPane.add(panel1B);
6240            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6241            //Provide for retrieval of names of previously saved signal heads
6242
6243            JPanel panel2 = new JPanel(new FlowLayout());
6244            JLabel shTitle = new JLabel(Bundle.getMessage("SignalHeads"));
6245            panel2.add(shTitle);
6246            panel2.add(new JLabel("   "));
6247            panel2.add(getSaved3WaySignalHeads = new JButton(Bundle.getMessage("GetSaved")));
6248            getSaved3WaySignalHeads.addActionListener(this::getSaved3WaySignals);
6249            getSaved3WaySignalHeads.setToolTipText(Bundle.getMessage("GetSavedHint"));
6250            theContentPane.add(panel2);
6251
6252            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6253            JPanel panel2a = new JPanel(new FlowLayout());
6254            panel2a.add(new JLabel("   "));
6255            panel2a.add(setPlaceAllHeads);
6256            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
6257            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
6258                boolean isSelected = setPlaceAllHeads.isSelected();
6259                //(de)select all checkboxes
6260                setA13WayHead.setSelected(isSelected);
6261                setA23WayHead.setSelected(isSelected);
6262                setA33WayHead.setSelected(isSelected);
6263                setB3WayHead.setSelected(isSelected);
6264                setC3WayHead.setSelected(isSelected);
6265                setD3WayHead.setSelected(isSelected);
6266            });
6267            panel2a.add(new JLabel("  "));
6268            panel2a.add(setupAllLogic);
6269            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
6270            setupAllLogic.addActionListener((ActionEvent e) -> {
6271                boolean isSelected = setupAllLogic.isSelected();
6272                //(de)select all checkboxes
6273                setupA13WayLogic.setSelected(isSelected);
6274                setupA23WayLogic.setSelected(isSelected);
6275                setupA33WayLogic.setSelected(isSelected);
6276                setupB3WayLogic.setSelected(isSelected);
6277                setupC3WayLogic.setSelected(isSelected);
6278                setupD3WayLogic.setSelected(isSelected);
6279            });
6280            theContentPane.add(panel2a);
6281            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6282
6283            //Signal heads located at turnout A
6284            JPanel panel20 = new JPanel(new FlowLayout());
6285            panel20.add(new JLabel(Bundle.getMessage("SignalLocated")
6286                    + " " + Bundle.getMessage("BeanNameTurnout") + " A "));
6287            theContentPane.add(panel20);
6288
6289            JPanel panel21 = new JPanel(new FlowLayout());
6290            panel21.add(new JLabel(Bundle.getMessage("MakeLabel",
6291                    throatString + " - "
6292                    + continuingString)));
6293            panel21.add(a1_3WaySignalHeadComboBox);
6294            a1_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6295            theContentPane.add(panel21);
6296
6297            JPanel panel22 = new JPanel(new FlowLayout());
6298            panel22.add(new JLabel("   "));
6299            panel22.add(setA13WayHead);
6300            setA13WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6301            panel22.add(new JLabel("  "));
6302            panel22.add(setupA13WayLogic);
6303            setupA13WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6304            theContentPane.add(panel22);
6305
6306            JPanel panel23 = new JPanel(new FlowLayout());
6307            panel23.add(new JLabel(Bundle.getMessage("MakeLabel",
6308                    throatString + " - "
6309                    + divergingAString)));
6310            panel23.add(a2_3WaySignalHeadComboBox);
6311            a2_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6312            theContentPane.add(panel23);
6313
6314            JPanel panel24 = new JPanel(new FlowLayout());
6315            panel24.add(new JLabel("   "));
6316            panel24.add(setA23WayHead);
6317            setA23WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6318            panel24.add(new JLabel("  "));
6319            panel24.add(setupA23WayLogic);
6320            setupA23WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6321            theContentPane.add(panel24);
6322
6323            JPanel panel25 = new JPanel(new FlowLayout());
6324            panel25.add(new JLabel(Bundle.getMessage("MakeLabel",
6325                    throatString + " - "
6326                    + divergingBString)));
6327            panel25.add(a3_3WaySignalHeadComboBox);
6328            a3_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6329            theContentPane.add(panel25);
6330
6331            JPanel panel26 = new JPanel(new FlowLayout());
6332            panel26.add(new JLabel("   "));
6333            panel26.add(setA33WayHead);
6334            setA33WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6335            panel26.add(new JLabel("  "));
6336            panel26.add(setupA33WayLogic);
6337            setupA33WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6338            theContentPane.add(panel26);
6339
6340            JPanel panel31 = new JPanel(new FlowLayout());
6341            panel31.add(new JLabel(Bundle.getMessage("MakeLabel",
6342                    divergingAString)));
6343            panel31.add(b_3WaySignalHeadComboBox);
6344            b_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6345            theContentPane.add(panel31);
6346
6347            JPanel panel32 = new JPanel(new FlowLayout());
6348            panel32.add(new JLabel("   "));
6349            panel32.add(setB3WayHead);
6350            setB3WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6351            panel32.add(new JLabel("  "));
6352            panel32.add(setupB3WayLogic);
6353            setupB3WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6354            theContentPane.add(panel32);
6355            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6356            //Signal heads located at turnout B
6357
6358            JPanel panel40 = new JPanel(new FlowLayout());
6359            panel40.add(new JLabel(Bundle.getMessage("SignalLocated")
6360                    + " " + Bundle.getMessage("BeanNameTurnout") + " B "));
6361            theContentPane.add(panel40);
6362
6363            JPanel panel41 = new JPanel(new FlowLayout());
6364            panel41.add(new JLabel(Bundle.getMessage("MakeLabel",
6365                    continuingString)));
6366            panel41.add(c_3WaySignalHeadComboBox);
6367            c_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6368            theContentPane.add(panel41);
6369
6370            JPanel panel42 = new JPanel(new FlowLayout());
6371            panel42.add(new JLabel("   "));
6372            panel42.add(setC3WayHead);
6373            setC3WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6374            panel42.add(new JLabel("  "));
6375            panel42.add(setupC3WayLogic);
6376            setupC3WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6377            theContentPane.add(panel42);
6378
6379            JPanel panel43 = new JPanel(new FlowLayout());
6380            panel43.add(new JLabel(Bundle.getMessage("MakeLabel",
6381                    divergingBString)));
6382            panel43.add(d_3WaySignalHeadComboBox);
6383            d_3WaySignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
6384            theContentPane.add(panel43);
6385
6386            JPanel panel44 = new JPanel(new FlowLayout());
6387            panel44.add(new JLabel("   "));
6388            panel44.add(setD3WayHead);
6389            setD3WayHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
6390            panel44.add(new JLabel("  "));
6391            panel44.add(setupD3WayLogic);
6392            setupD3WayLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
6393            theContentPane.add(panel44);
6394            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
6395            //buttons
6396
6397            JPanel panel6 = new JPanel(new FlowLayout());
6398            panel6.add(change3WaySignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
6399            change3WaySignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
6400            change3WaySignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
6401            panel6.add(new JLabel("   "));
6402            panel6.add(set3WaySignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
6403            set3WaySignalsDone.addActionListener(this::set3WaySignalsDonePressed);
6404            set3WaySignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
6405
6406            panel6.add(set3WaySignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
6407            set3WaySignalsCancel.addActionListener(this::set3WaySignalsCancelPressed);
6408            set3WaySignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
6409            theContentPane.add(panel6);
6410
6411            //make this button the default button (return or enter activates)
6412            JRootPane rootPane = SwingUtilities.getRootPane(set3WaySignalsDone);
6413            if (rootPane != null) {
6414                rootPane.setDefaultButton(set3WaySignalsDone);
6415            }
6416
6417            setSignalsAt3WayTurnoutFrame.addWindowListener(new WindowAdapter() {
6418                @Override
6419                public void windowClosing(WindowEvent e) {
6420                    set3WaySignalsCancelPressed(null);
6421                }
6422            });
6423        }
6424        setPlaceAllHeads.setSelected(false);
6425        setupAllLogic.setSelected(false);
6426
6427        turnoutAComboBox.setVisible(!setSignalsAt3WayTurnoutFromMenuFlag);
6428        turnoutBComboBox.setVisible(!setSignalsAt3WayTurnoutFromMenuFlag);
6429        if (setSignalsAt3WayTurnoutFromMenuFlag) {
6430            turnoutANameLabel.setText(Bundle.getMessage("MakeLabel",
6431                    Bundle.getMessage("BeanNameTurnout") + " A")
6432                    + turnoutAComboBox.getSelectedItemDisplayName());
6433            turnoutBNameLabel.setText(Bundle.getMessage("MakeLabel",
6434                    Bundle.getMessage("BeanNameTurnout") + " B")
6435                    + turnoutBComboBox.getSelectedItemDisplayName());
6436            getSaved3WaySignals(null);
6437        } else {
6438            turnoutANameLabel.setText(Bundle.getMessage("MakeLabel",
6439                    Bundle.getMessage("TurnoutAName")));
6440            turnoutBNameLabel.setText(Bundle.getMessage("MakeLabel",
6441                    Bundle.getMessage("TurnoutBName")));
6442        }
6443
6444        if (!setSignalsAt3WayTurnoutOpenFlag) {
6445            setSignalsAt3WayTurnoutFrame.setPreferredSize(null);
6446            setSignalsAt3WayTurnoutFrame.pack();
6447            setSignalsAt3WayTurnoutOpenFlag = true;
6448        }
6449        setSignalsAt3WayTurnoutFrame.setVisible(true);
6450    }   //setSignalsAt3WayTurnout
6451
6452    private void getSaved3WaySignals(ActionEvent a) {
6453        if (!get3WayTurnoutInformation()) {
6454            return;
6455        }
6456        a1_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalA1());
6457        a2_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalA2());
6458        a3_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalA3());
6459        b_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutA.getSignalC1());
6460        c_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutB.getSignalB1());
6461        d_3WaySignalHeadComboBox.setSelectedItem(layoutTurnoutB.getSignalC1());
6462    }
6463
6464    private void set3WaySignalsCancelPressed(ActionEvent a) {
6465        setSignalsAt3WayTurnoutOpenFlag = false;
6466        setSignalsAt3WayTurnoutFrame.setVisible(false);
6467    }
6468
6469    private boolean get3WayTurnoutInformation() {
6470        HitPointType type = HitPointType.NONE;
6471        Object connect = null;
6472        turnoutA = null;
6473        turnoutB = null;
6474        layoutTurnoutA = null;
6475        layoutTurnoutB = null;
6476
6477        String str = turnoutAComboBox.getSelectedItemDisplayName();
6478        if ((str == null) || str.isEmpty()) {
6479            //turnout A not entered, test turnout B
6480            str = turnoutBComboBox.getSelectedItemDisplayName();
6481            if ((str == null) || str.isEmpty()) {
6482                //no entries in turnout fields
6483                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6484                        Bundle.getMessage("SignalsError1"),
6485                        Bundle.getMessage("ErrorTitle"),
6486                        JmriJOptionPane.ERROR_MESSAGE);
6487                return false;
6488            }
6489            turnoutB = InstanceManager.turnoutManagerInstance().getTurnout(str);
6490            if (turnoutB == null) {
6491                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6492                        Bundle.getMessage("SignalsError2",
6493                                new Object[]{str}), Bundle.getMessage("ErrorTitle"),
6494                        JmriJOptionPane.ERROR_MESSAGE);
6495                return false;
6496            }
6497            String uname = turnoutB.getUserName();
6498            if ((uname == null) || uname.isEmpty()
6499                    || !uname.equals(str)) {
6500                turnoutBComboBox.setSelectedItem(turnoutB);
6501            }
6502            layoutTurnoutB = getLayoutTurnoutFromTurnout(turnoutB, false, str, setSignalsAt3WayTurnoutFrame);
6503            if (layoutTurnoutB == null) {
6504                return false;
6505            }
6506            //have turnout B and layout turnout B - look for turnout A
6507            connectorTrack = (TrackSegment) layoutTurnoutB.getConnectA();
6508            if (connectorTrack == null) {
6509                //Inform user of error, and terminate
6510                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6511                        Bundle.getMessage("SignalsError19"),
6512                        Bundle.getMessage("ErrorTitle"),
6513                        JmriJOptionPane.ERROR_MESSAGE);
6514                return false;
6515            }
6516            type = connectorTrack.getType1();
6517            connect = connectorTrack.getConnect1();
6518            if (connect == layoutTurnoutB) {
6519                type = connectorTrack.getType2();
6520                connect = connectorTrack.getConnect2();
6521            }
6522            if ((type != HitPointType.TURNOUT_B) || (connect == null)) {
6523                //Not two turnouts connected as required by a single Track Segment
6524                //Inform user of error and terminate
6525                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6526                        Bundle.getMessage("SignalsError19"),
6527                        Bundle.getMessage("ErrorTitle"),
6528                        JmriJOptionPane.ERROR_MESSAGE);
6529                return false;
6530            }
6531            layoutTurnoutA = (LayoutTurnout) connect;
6532            turnoutA = layoutTurnoutA.getTurnout();
6533            if (turnoutA == null) {
6534                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6535                        Bundle.getMessage("SignalsError19"),
6536                        Bundle.getMessage("ErrorTitle"),
6537                        JmriJOptionPane.ERROR_MESSAGE);
6538                return false;
6539            }
6540            turnoutAComboBox.setSelectedItem(turnoutA);
6541        } else {
6542            //something was entered in the turnout A field
6543            turnoutA = InstanceManager.turnoutManagerInstance().getTurnout(str);
6544            if (turnoutA == null) {
6545                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6546                        Bundle.getMessage("SignalsError2",
6547                                new Object[]{str}), Bundle.getMessage("ErrorTitle"),
6548                        JmriJOptionPane.ERROR_MESSAGE);
6549                return false;
6550            }
6551            String uname = turnoutA.getUserName();
6552            if ((uname == null) || uname.isEmpty()
6553                    || !uname.equals(str)) {
6554                turnoutAComboBox.setSelectedItem(turnoutA);
6555            }
6556            //have turnout A - get corresponding layoutTurnout
6557            layoutTurnoutA = getLayoutTurnoutFromTurnout(turnoutA, false, str, setSignalsAt3WayTurnoutFrame);
6558            if (layoutTurnoutA == null) {
6559                return false;
6560            }
6561            turnoutAComboBox.setSelectedItem(layoutTurnoutA.getTurnout());
6562            //have turnout A and layout turnout A - was something entered for turnout B
6563            str = turnoutBComboBox.getSelectedItemDisplayName();
6564            if ((str == null) || str.isEmpty()) {
6565                //no entry for turnout B
6566                connectorTrack = (TrackSegment) layoutTurnoutA.getConnectB();
6567                if (connectorTrack == null) {
6568                    //Inform user of error, and terminate
6569                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6570                            Bundle.getMessage("SignalsError19"),
6571                            Bundle.getMessage("ErrorTitle"),
6572                            JmriJOptionPane.ERROR_MESSAGE);
6573                    return false;
6574                }
6575                type = connectorTrack.getType1();
6576                connect = connectorTrack.getConnect1();
6577                if (connect == layoutTurnoutA) {
6578                    type = connectorTrack.getType2();
6579                    connect = connectorTrack.getConnect2();
6580                }
6581                if ((type != HitPointType.TURNOUT_A) || (connect == null)) {
6582                    // Not two turnouts connected with the throat of B connected to the continuing of A
6583                    //  by a single Track Segment.  Inform user of error and terminate.
6584                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6585                            Bundle.getMessage("SignalsError19"),
6586                            Bundle.getMessage("ErrorTitle"),
6587                            JmriJOptionPane.ERROR_MESSAGE);
6588                    return false;
6589                }
6590                layoutTurnoutB = (LayoutTurnout) connect;
6591                turnoutB = layoutTurnoutB.getTurnout();
6592                if (turnoutB == null) {
6593                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6594                            Bundle.getMessage("SignalsError19"),
6595                            Bundle.getMessage("ErrorTitle"),
6596                            JmriJOptionPane.ERROR_MESSAGE);
6597                    return false;
6598                }
6599                turnoutBComboBox.setSelectedItem(turnoutB);
6600            } else {
6601                //turnout B entered also
6602                turnoutB = InstanceManager.turnoutManagerInstance().getTurnout(str);
6603                if (turnoutB == null) {
6604                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6605                            Bundle.getMessage("SignalsError2",
6606                                    new Object[]{str}), Bundle.getMessage("ErrorTitle"),
6607                            JmriJOptionPane.ERROR_MESSAGE);
6608                    return false;
6609                }
6610                uname = turnoutB.getUserName();
6611                if ((uname == null) || uname.isEmpty()
6612                        || !uname.equals(str)) {
6613                    turnoutBComboBox.setSelectedItem(turnoutB);
6614                }
6615                layoutTurnoutB = getLayoutTurnoutFromTurnout(turnoutB, false, str, setSignalsAt3WayTurnoutFrame);
6616                if (layoutTurnoutB == null) {
6617                    return false;
6618                }
6619                turnoutBComboBox.setSelectedItem(layoutTurnoutB.getTurnout());
6620                //check that layout turnout A and layout turnout B are connected as required
6621                if (layoutTurnoutA.getConnectB() != layoutTurnoutB.getConnectA()) {
6622                    //Not two turnouts connected as required by a single Track Segment
6623                    //Inform user of error and terminate
6624                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6625                            Bundle.getMessage("SignalsError19"),
6626                            Bundle.getMessage("ErrorTitle"),
6627                            JmriJOptionPane.ERROR_MESSAGE);
6628                    return false;
6629                }
6630                connectorTrack = (TrackSegment) layoutTurnoutA.getConnectB();
6631            }
6632        }
6633        return true;
6634    }   //get3WayTurnoutInformation
6635
6636    private void set3WaySignalsDonePressed(ActionEvent a) {
6637        //process turnout names
6638        if (!get3WayTurnoutInformation()) {
6639            return;
6640        }
6641        //process signal head names
6642        if (!get3WaySignalHeadInformation()) {
6643            return;
6644        }
6645        //place signals as requested at turnout A
6646        String signalHeadName = a1_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6647        if (signalHeadName == null) {
6648            signalHeadName = "";
6649        }
6650        if (setA13WayHead.isSelected()) {
6651            if (isHeadOnPanel(a13WayHead)
6652                    && (a13WayHead != getHeadFromName(layoutTurnoutA.getSignalA1Name()))) {
6653                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6654                        Bundle.getMessage("SignalsError6",
6655                                new Object[]{signalHeadName}),
6656                        Bundle.getMessage("ErrorTitle"),
6657                        JmriJOptionPane.ERROR_MESSAGE);
6658                return;
6659            } else {
6660                removeSignalHeadFromPanel(layoutTurnoutA.getSignalA1Name());
6661                place3WayThroatContinuing();
6662                removeAssignment(a13WayHead);
6663                layoutTurnoutA.setSignalA1Name(signalHeadName);
6664                needRedraw = true;
6665            }
6666        } else {
6667            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a13WayHead, layoutTurnoutA);
6668            if (assigned == LayoutTurnout.Geometry.NONE) {
6669                if (isHeadOnPanel(a13WayHead)
6670                        && isHeadAssignedAnywhere(a13WayHead)) {
6671                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6672                            Bundle.getMessage("SignalsError8",
6673                                    new Object[]{signalHeadName}),
6674                            Bundle.getMessage("ErrorTitle"),
6675                            JmriJOptionPane.ERROR_MESSAGE);
6676                    return;
6677                } else {
6678                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalA1Name());
6679                    removeAssignment(a13WayHead);
6680                    layoutTurnoutA.setSignalA1Name(signalHeadName);
6681                }
6682                //} else if (assigned != A1) {
6683                //need to figure out what to do in this case.
6684            }
6685        }
6686
6687        signalHeadName = a2_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6688        if (signalHeadName == null) {
6689            signalHeadName = "";
6690        }
6691        if ((setA23WayHead.isSelected()) && (a23WayHead != null)) {
6692            if (isHeadOnPanel(a23WayHead)
6693                    && (a23WayHead != getHeadFromName(layoutTurnoutA.getSignalA2Name()))) {
6694                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6695                        Bundle.getMessage("SignalsError6",
6696                                new Object[]{signalHeadName}),
6697                        Bundle.getMessage("ErrorTitle"),
6698                        JmriJOptionPane.ERROR_MESSAGE);
6699                return;
6700            } else {
6701                removeSignalHeadFromPanel(layoutTurnoutA.getSignalA2Name());
6702                place3WayThroatDivergingA();
6703                removeAssignment(a23WayHead);
6704                layoutTurnoutA.setSignalA2Name(signalHeadName);
6705                needRedraw = true;
6706            }
6707        } else if (a23WayHead != null) {
6708            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a23WayHead, layoutTurnoutA);
6709            if (assigned == LayoutTurnout.Geometry.NONE) {
6710                if (isHeadOnPanel(a23WayHead)
6711                        && isHeadAssignedAnywhere(a23WayHead)) {
6712                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6713                            Bundle.getMessage("SignalsError8",
6714                                    new Object[]{signalHeadName}),
6715                            Bundle.getMessage("ErrorTitle"),
6716                            JmriJOptionPane.ERROR_MESSAGE);
6717                    return;
6718                } else {
6719                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalA2Name());
6720                    removeAssignment(a23WayHead);
6721                    layoutTurnoutA.setSignalA2Name(signalHeadName);
6722                }
6723                //} else if (assigned != A2) {
6724                //need to figure out what to do in this case.
6725            }
6726        } else {  //a23WayHead is always null here
6727            removeSignalHeadFromPanel(layoutTurnoutA.getSignalA2Name());
6728            layoutTurnoutA.setSignalA2Name("");
6729        }
6730
6731        signalHeadName = a3_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6732        if (signalHeadName == null) {
6733            signalHeadName = "";
6734        }
6735        if ((setA33WayHead.isSelected()) && (a33WayHead != null)) {
6736            if (isHeadOnPanel(a33WayHead)
6737                    && (a33WayHead != getHeadFromName(layoutTurnoutA.getSignalA3Name()))) {
6738                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6739                        Bundle.getMessage("SignalsError6",
6740                                new Object[]{signalHeadName}),
6741                        Bundle.getMessage("ErrorTitle"),
6742                        JmriJOptionPane.ERROR_MESSAGE);
6743                return;
6744            } else {
6745                removeSignalHeadFromPanel(layoutTurnoutA.getSignalA3Name());
6746                place3WayThroatDivergingB();
6747                removeAssignment(a33WayHead);
6748                layoutTurnoutA.setSignalA3Name(signalHeadName);
6749                needRedraw = true;
6750            }
6751        } else if (a33WayHead != null) {
6752            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a33WayHead, layoutTurnoutA);
6753            if (assigned == LayoutTurnout.Geometry.NONE) {
6754                if (isHeadOnPanel(a33WayHead)
6755                        && isHeadAssignedAnywhere(a33WayHead)) {
6756                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6757                            Bundle.getMessage("SignalsError8",
6758                                    new Object[]{signalHeadName}),
6759                            Bundle.getMessage("ErrorTitle"),
6760                            JmriJOptionPane.ERROR_MESSAGE);
6761                    return;
6762                } else {
6763                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalA3Name());
6764                    removeAssignment(a33WayHead);
6765                    layoutTurnoutA.setSignalA3Name(signalHeadName);
6766                }
6767                //} else if (assigned != A3) {
6768                //need to figure out what to do in this case.
6769            }
6770        } else {  //a23WayHead is always null here
6771            removeSignalHeadFromPanel(layoutTurnoutA.getSignalA3Name());
6772            layoutTurnoutA.setSignalA3Name("");
6773        }
6774
6775        signalHeadName = b_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6776        if (signalHeadName == null) {
6777            signalHeadName = "";
6778        }
6779        if (setB3WayHead.isSelected()) {
6780            if (isHeadOnPanel(b3WayHead)
6781                    && (b3WayHead != getHeadFromName(layoutTurnoutA.getSignalC1Name()))) {
6782                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6783                        Bundle.getMessage("SignalsError6",
6784                                new Object[]{signalHeadName}),
6785                        Bundle.getMessage("ErrorTitle"),
6786                        JmriJOptionPane.ERROR_MESSAGE);
6787                return;
6788            } else {
6789                removeSignalHeadFromPanel(layoutTurnoutA.getSignalC1Name());
6790                place3WayDivergingA();
6791                removeAssignment(b3WayHead);
6792                layoutTurnoutA.setSignalC1Name(signalHeadName);
6793                needRedraw = true;
6794            }
6795        } else {
6796            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b3WayHead, layoutTurnoutA);
6797            if (assigned == LayoutTurnout.Geometry.NONE) {
6798                if (isHeadOnPanel(b3WayHead)
6799                        && isHeadAssignedAnywhere(b3WayHead)) {
6800                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6801                            Bundle.getMessage("SignalsError8",
6802                                    new Object[]{signalHeadName}),
6803                            Bundle.getMessage("ErrorTitle"),
6804                            JmriJOptionPane.ERROR_MESSAGE);
6805                    return;
6806                } else {
6807                    removeSignalHeadFromPanel(layoutTurnoutA.getSignalC1Name());
6808                    removeAssignment(b3WayHead);
6809                    layoutTurnoutA.setSignalC1Name(signalHeadName);
6810                }
6811                //} else if (assigned != A1) {
6812                //need to figure out what to do in this case.
6813            }
6814        }
6815
6816        //place signals as requested at Turnout C
6817        signalHeadName = c_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6818        if (signalHeadName == null) {
6819            signalHeadName = "";
6820        }
6821        if (setC3WayHead.isSelected()) {
6822            if (isHeadOnPanel(c3WayHead)
6823                    && (c3WayHead != getHeadFromName(layoutTurnoutB.getSignalB1Name()))) {
6824                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6825                        Bundle.getMessage("SignalsError6",
6826                                new Object[]{signalHeadName}),
6827                        Bundle.getMessage("ErrorTitle"),
6828                        JmriJOptionPane.ERROR_MESSAGE);
6829                return;
6830            } else {
6831                removeSignalHeadFromPanel(layoutTurnoutB.getSignalB1Name());
6832                place3WayContinuing();
6833                removeAssignment(c3WayHead);
6834                layoutTurnoutB.setSignalB1Name(signalHeadName);
6835                needRedraw = true;
6836            }
6837        } else {
6838            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c3WayHead, layoutTurnoutB);
6839            if (assigned == LayoutTurnout.Geometry.NONE) {
6840                if (isHeadOnPanel(c3WayHead)
6841                        && isHeadAssignedAnywhere(c3WayHead)) {
6842                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6843                            Bundle.getMessage("SignalsError8",
6844                                    new Object[]{signalHeadName}),
6845                            Bundle.getMessage("ErrorTitle"),
6846                            JmriJOptionPane.ERROR_MESSAGE);
6847                    return;
6848                } else {
6849                    removeSignalHeadFromPanel(layoutTurnoutB.getSignalB1Name());
6850                    removeAssignment(c3WayHead);
6851                    layoutTurnoutB.setSignalB1Name(signalHeadName);
6852                }
6853                //} else if (assigned != B1) {
6854                //need to figure out what to do in this case.
6855            }
6856        }
6857
6858        signalHeadName = d_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6859        if (signalHeadName == null) {
6860            signalHeadName = "";
6861        }
6862        if (setD3WayHead.isSelected()) {
6863            if (isHeadOnPanel(d3WayHead)
6864                    && (d3WayHead != getHeadFromName(layoutTurnoutB.getSignalC1Name()))) {
6865                JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6866                        Bundle.getMessage("SignalsError6",
6867                                new Object[]{signalHeadName}),
6868                        Bundle.getMessage("ErrorTitle"),
6869                        JmriJOptionPane.ERROR_MESSAGE);
6870                return;
6871            } else {
6872                removeSignalHeadFromPanel(layoutTurnoutB.getSignalC1Name());
6873                place3WayDivergingB();
6874                removeAssignment(d3WayHead);
6875                layoutTurnoutB.setSignalC1Name(signalHeadName);
6876                needRedraw = true;
6877            }
6878        } else {
6879            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d3WayHead, layoutTurnoutB);
6880            if (assigned == LayoutTurnout.Geometry.NONE) {
6881                if (isHeadOnPanel(d3WayHead)
6882                        && isHeadAssignedAnywhere(d3WayHead)) {
6883                    JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
6884                            Bundle.getMessage("SignalsError8",
6885                                    new Object[]{signalHeadName}),
6886                            Bundle.getMessage("ErrorTitle"),
6887                            JmriJOptionPane.ERROR_MESSAGE);
6888                    return;
6889                } else {
6890                    removeSignalHeadFromPanel(layoutTurnoutB.getSignalC1Name());
6891                    removeAssignment(d3WayHead);
6892                    layoutTurnoutB.setSignalC1Name(signalHeadName);
6893                }
6894                //} else if (assigned != C1) {
6895                //need to figure out what to do in this case.
6896            }
6897        }
6898        //setup Logic if requested and enough information is available
6899        if (setupA13WayLogic.isSelected()) {
6900            set3WayLogicThroatContinuing();
6901        }
6902        if ((a23WayHead != null) && setupA23WayLogic.isSelected()) {
6903            set3WayLogicThroatDivergingA();
6904        }
6905        if ((a33WayHead != null) && setupA33WayLogic.isSelected()) {
6906            set3WayLogicThroatDivergingB();
6907        }
6908        if (setupB3WayLogic.isSelected()) {
6909            set3WayLogicDivergingA();
6910        }
6911        if (setupC3WayLogic.isSelected()) {
6912            set3WayLogicContinuing();
6913        }
6914        if (setupD3WayLogic.isSelected()) {
6915            set3WayLogicDivergingB();
6916        }
6917        //link the two turnouts
6918        signalHeadName = turnoutBComboBox.getSelectedItemDisplayName();
6919        if (signalHeadName == null) {
6920            signalHeadName = "";
6921        }
6922        layoutTurnoutA.setLinkedTurnoutName(signalHeadName);
6923        layoutTurnoutA.setLinkType(LayoutTurnout.LinkType.FIRST_3_WAY);
6924        signalHeadName = turnoutAComboBox.getSelectedItemDisplayName();
6925        if (signalHeadName == null) {
6926            signalHeadName = "";
6927        }
6928        layoutTurnoutB.setLinkedTurnoutName(signalHeadName);
6929        layoutTurnoutB.setLinkType(LayoutTurnout.LinkType.SECOND_3_WAY);
6930        //finish up
6931        setSignalsAt3WayTurnoutOpenFlag = false;
6932        setSignalsAt3WayTurnoutFrame.setVisible(false);
6933        if (needRedraw) {
6934            layoutEditor.redrawPanel();
6935            needRedraw = false;
6936            layoutEditor.setDirty();
6937        }
6938    }   //set3WaySignalsDonePressed
6939
6940    private boolean get3WaySignalHeadInformation() {
6941        a13WayHead = getSignalHeadFromEntry(a1_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6942        if (a13WayHead == null) {
6943            return false;
6944        }
6945        a23WayHead = getSignalHeadFromEntry(a2_3WaySignalHeadComboBox, false, setSignalsAt3WayTurnoutFrame);
6946        a33WayHead = getSignalHeadFromEntry(a3_3WaySignalHeadComboBox, false, setSignalsAt3WayTurnoutFrame);
6947        if (((a23WayHead == null) && (a33WayHead != null)) || ((a33WayHead == null)
6948                && (a23WayHead != null))) {
6949            return false;
6950        }
6951        b3WayHead = getSignalHeadFromEntry(b_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6952        if (b3WayHead == null) {
6953            return false;
6954        }
6955        c3WayHead = getSignalHeadFromEntry(c_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6956        if (c3WayHead == null) {
6957            return false;
6958        }
6959        d3WayHead = getSignalHeadFromEntry(d_3WaySignalHeadComboBox, true, setSignalsAt3WayTurnoutFrame);
6960        if (d3WayHead == null) {
6961            return false;
6962        }
6963        return true;
6964    }
6965
6966    private void place3WayThroatContinuing() {
6967        if (testIcon == null) {
6968            testIcon = signalIconEditor.getIcon(0);
6969        }
6970        String signalHeadName = a1_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6971        if (signalHeadName == null) {
6972            signalHeadName = "";
6973        }
6974        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
6975
6976        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
6977        Point2D coordsA = layoutTurnoutAView.getCoordsA();
6978        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
6979
6980        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
6981        Point2D delta = new Point2D.Double(-shift, -shift);
6982
6983        delta = MathUtil.rotateDEG(delta, aDirDEG);
6984        Point2D where = MathUtil.add(coordsA, delta);
6985        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
6986    }
6987
6988    private void place3WayThroatDivergingA() {
6989        if (testIcon == null) {
6990            testIcon = signalIconEditor.getIcon(0);
6991        }
6992        String signalHeadName = a2_3WaySignalHeadComboBox.getSelectedItemDisplayName();
6993        if (signalHeadName == null) {
6994            signalHeadName = "";
6995        }
6996        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
6997
6998        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
6999        Point2D coordsA = layoutTurnoutAView.getCoordsA();
7000        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
7001
7002        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
7003        Point2D delta = new Point2D.Double(+shift, -shift);
7004
7005        delta = MathUtil.rotateDEG(delta, aDirDEG);
7006        Point2D where = MathUtil.add(coordsA, delta);
7007        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
7008    }
7009
7010    private void place3WayThroatDivergingB() {
7011        if (testIcon == null) {
7012            testIcon = signalIconEditor.getIcon(0);
7013        }
7014        String signalHeadName = a3_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7015        if (signalHeadName == null) {
7016            signalHeadName = "";
7017        }
7018        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7019
7020        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
7021        Point2D coordsA = layoutTurnoutAView.getCoordsA();
7022        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
7023
7024        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
7025        Point2D delta = new Point2D.Double(+3.0 * shift, -shift);
7026
7027        delta = MathUtil.rotateDEG(delta, aDirDEG);
7028        Point2D where = MathUtil.add(coordsA, delta);
7029        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
7030    }
7031
7032    private void place3WayDivergingA() {
7033        if (testIcon == null) {
7034            testIcon = signalIconEditor.getIcon(0);
7035        }
7036        String signalHeadName = b_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7037        if (signalHeadName == null) {
7038            signalHeadName = "";
7039        }
7040        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7041
7042        LayoutTurnoutView layoutTurnoutAView = layoutEditor.getLayoutTurnoutView(layoutTurnoutA);
7043        Point2D coordsB = layoutTurnoutAView.getCoordsB();
7044        Point2D coordsC = layoutTurnoutAView.getCoordsC();
7045        Point2D coordsCenter = layoutTurnoutAView.getCoordsCenter();
7046
7047        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
7048        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
7049        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
7050        double shiftX = shift;
7051        if (diffDirDEG >= 0.0) {
7052            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
7053        }
7054        Point2D delta = new Point2D.Double(shiftX, -shift);
7055
7056        delta = MathUtil.rotateDEG(delta, cDirDEG);
7057        Point2D where = MathUtil.add(coordsC, delta);
7058        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
7059    }
7060
7061    private void place3WayContinuing() {
7062        if (testIcon == null) {
7063            testIcon = signalIconEditor.getIcon(0);
7064        }
7065        String signalHeadName = c_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7066        if (signalHeadName == null) {
7067            signalHeadName = "";
7068        }
7069        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7070
7071        LayoutTurnoutView layoutTurnoutBView = layoutEditor.getLayoutTurnoutView(layoutTurnoutB);
7072        Point2D coordsB = layoutTurnoutBView.getCoordsB();
7073        Point2D coordsC = layoutTurnoutBView.getCoordsC();
7074        Point2D coordsCenter = layoutTurnoutBView.getCoordsCenter();
7075
7076        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
7077        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
7078        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
7079        double shiftX = shift;
7080        if (diffDirDEG >= 0.0) {
7081            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
7082        }
7083        Point2D delta = new Point2D.Double(shiftX, -shift);
7084
7085        delta = MathUtil.rotateDEG(delta, bDirDEG);
7086        Point2D where = MathUtil.add(coordsB, delta);
7087        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
7088    }
7089
7090    private void place3WayDivergingB() {
7091        if (testIcon == null) {
7092            testIcon = signalIconEditor.getIcon(0);
7093        }
7094        String signalHeadName = d_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7095        if (signalHeadName == null) {
7096            signalHeadName = "";
7097        }
7098        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
7099
7100        LayoutTurnoutView layoutTurnoutBView = layoutEditor.getLayoutTurnoutView(layoutTurnoutB);
7101        Point2D coordsC = layoutTurnoutBView.getCoordsC();
7102        Point2D coordsB = layoutTurnoutBView.getCoordsB();
7103        Point2D coordsCenter = layoutTurnoutBView.getCoordsCenter();
7104
7105        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
7106        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
7107        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
7108        double shiftX = shift;
7109        if (diffDirDEG >= 0.0) {
7110            shiftX += shift * Math.cos(Math.toRadians(diffDirDEG));
7111        }
7112        Point2D delta = new Point2D.Double(shiftX, -shift);
7113
7114        delta = MathUtil.rotateDEG(delta, cDirDEG);
7115        Point2D where = MathUtil.add(coordsC, delta);
7116        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
7117    }
7118
7119    private void set3WayLogicThroatContinuing() {
7120        TrackSegment track = (TrackSegment) layoutTurnoutB.getConnectB();
7121        if (track == null) {
7122            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7123                    Bundle.getMessage("InfoMessage7"),
7124                    Bundle.getMessage("MessageTitle"),
7125                    JmriJOptionPane.INFORMATION_MESSAGE);
7126            return;
7127        }
7128        LayoutBlock block = track.getLayoutBlock();
7129        if (block == null) {
7130            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7131                    Bundle.getMessage("InfoMessage6"),
7132                    Bundle.getMessage("MessageTitle"),
7133                    JmriJOptionPane.INFORMATION_MESSAGE);
7134            return;
7135        }
7136        Sensor occupancy = block.getOccupancySensor();
7137        if (occupancy == null) {
7138            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7139                    Bundle.getMessage("InfoMessage4",
7140                            new Object[]{block.getUserName()}),
7141                    Bundle.getMessage("MessageTitle"),
7142                    JmriJOptionPane.INFORMATION_MESSAGE);
7143            return;
7144        }
7145        String signalHeadName = a1_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7146        if (signalHeadName == null) {
7147            signalHeadName = "";
7148        }
7149        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutB,
7150                signalHeadName, setSignalsAt3WayTurnoutFrame);
7151        if ((nextHead == null) && (!reachedEndBumper())) {
7152            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7153                    Bundle.getMessage("InfoMessage5",
7154                            new Object[]{block.getUserName()}),
7155                    Bundle.getMessage("MessageTitle"),
7156                    JmriJOptionPane.INFORMATION_MESSAGE);
7157            return;
7158        }
7159        if (a23WayHead != null) {
7160            //set up logic for continuing head with 3 heads at throat
7161            if (!initializeBlockBossLogic(signalHeadName)) {
7162                return;
7163            }
7164            logic.setMode(BlockBossLogic.TRAILINGMAIN);
7165            logic.setTurnout(turnoutB.getDisplayName());
7166            logic.setSensor1(occupancy.getDisplayName());
7167            if (nextHead != null) {
7168                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7169            }
7170            if (auxSignal != null) {
7171                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7172            }
7173            String nearSensorName = setupNearLogix(layoutTurnoutA, true, a13WayHead);
7174            addNearSensorToLogic(nearSensorName);
7175            finalizeBlockBossLogic();
7176            return;
7177        }
7178        //only one head at the throat
7179        JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7180                Bundle.getMessage("InfoMessage9"),
7181                Bundle.getMessage("MessageTitle"),
7182                JmriJOptionPane.INFORMATION_MESSAGE);
7183        return;
7184    }   //set3WayLogicThroatContinuing
7185
7186    private void set3WayLogicThroatDivergingA() {
7187        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectC();
7188        if (track == null) {
7189            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7190                    Bundle.getMessage("InfoMessage7"),
7191                    Bundle.getMessage("MessageTitle"),
7192                    JmriJOptionPane.INFORMATION_MESSAGE);
7193            return;
7194        }
7195        LayoutBlock block = track.getLayoutBlock();
7196        if (block == null) {
7197            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7198                    Bundle.getMessage("InfoMessage6"),
7199                    Bundle.getMessage("MessageTitle"),
7200                    JmriJOptionPane.INFORMATION_MESSAGE);
7201            return;
7202        }
7203        Sensor occupancy = block.getOccupancySensor();
7204        if (occupancy == null) {
7205            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7206                    Bundle.getMessage("InfoMessage4",
7207                            new Object[]{block.getUserName()}),
7208                    Bundle.getMessage("MessageTitle"),
7209                    JmriJOptionPane.INFORMATION_MESSAGE);
7210            return;
7211        }
7212        String signalHeadName = a2_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7213        if (signalHeadName == null) {
7214            signalHeadName = "";
7215        }
7216        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7217                signalHeadName, setSignalsAt3WayTurnoutFrame);
7218        if ((nextHead == null) && (!reachedEndBumper())) {
7219            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7220                    Bundle.getMessage("InfoMessage5",
7221                            new Object[]{block.getUserName()}),
7222                    Bundle.getMessage("MessageTitle"),
7223                    JmriJOptionPane.INFORMATION_MESSAGE);
7224            return;
7225        }
7226        if (!initializeBlockBossLogic(signalHeadName)) {
7227            return;
7228        }
7229        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7230        logic.setTurnout(turnoutA.getDisplayName());
7231        logic.setSensor1(occupancy.getDisplayName());
7232        if (nextHead != null) {
7233            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7234        }
7235        if (auxSignal != null) {
7236            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7237        }
7238        if (!layoutTurnoutA.isMainlineC()) {
7239            logic.setLimitSpeed2(true);
7240        }
7241        finalizeBlockBossLogic();
7242    }   //set3WayLogicThroatDivergingA
7243
7244    private void set3WayLogicThroatDivergingB() {
7245        TrackSegment track = (TrackSegment) layoutTurnoutB.getConnectC();
7246        if (track == null) {
7247            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7248                    Bundle.getMessage("InfoMessage7"),
7249                    Bundle.getMessage("MessageTitle"),
7250                    JmriJOptionPane.INFORMATION_MESSAGE);
7251            return;
7252        }
7253        LayoutBlock block = track.getLayoutBlock();
7254        if (block == null) {
7255            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7256                    Bundle.getMessage("InfoMessage6"),
7257                    Bundle.getMessage("MessageTitle"),
7258                    JmriJOptionPane.INFORMATION_MESSAGE);
7259            return;
7260        }
7261        Sensor occupancy = block.getOccupancySensor();
7262        if (occupancy == null) {
7263            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7264                    Bundle.getMessage("InfoMessage4",
7265                            new Object[]{block.getUserName()}),
7266                    Bundle.getMessage("MessageTitle"),
7267                    JmriJOptionPane.INFORMATION_MESSAGE);
7268            return;
7269        }
7270        String signalHeadName = a3_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7271        if (signalHeadName == null) {
7272            signalHeadName = "";
7273        }
7274        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutB,
7275                signalHeadName, setSignalsAt3WayTurnoutFrame);
7276        if ((nextHead == null) && (!reachedEndBumper())) {
7277            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7278                    Bundle.getMessage("InfoMessage5",
7279                            new Object[]{block.getUserName()}),
7280                    Bundle.getMessage("MessageTitle"),
7281                    JmriJOptionPane.INFORMATION_MESSAGE);
7282            return;
7283        }
7284        if (!initializeBlockBossLogic(signalHeadName)) {
7285            return;
7286        }
7287        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7288        logic.setTurnout(turnoutB.getDisplayName());
7289        logic.setSensor1(occupancy.getDisplayName());
7290        if (nextHead != null) {
7291            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7292        }
7293        if (auxSignal != null) {
7294            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7295        }
7296        String nearSensorName = setupNearLogix(layoutTurnoutA, true, a33WayHead);
7297        addNearSensorToLogic(nearSensorName);
7298        if (!layoutTurnoutB.isMainlineC()) {
7299            logic.setLimitSpeed2(true);
7300        }
7301        finalizeBlockBossLogic();
7302    }   //set3WayLogicThroatDivergingB
7303
7304    private void set3WayLogicDivergingA() {
7305        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectA();
7306        if (track == null) {
7307            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7308                    Bundle.getMessage("InfoMessage7"),
7309                    Bundle.getMessage("MessageTitle"),
7310                    JmriJOptionPane.INFORMATION_MESSAGE);
7311            return;
7312        }
7313        LayoutBlock block = track.getLayoutBlock();
7314        if (block == null) {
7315            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7316                    Bundle.getMessage("InfoMessage6"),
7317                    Bundle.getMessage("MessageTitle"),
7318                    JmriJOptionPane.INFORMATION_MESSAGE);
7319            return;
7320        }
7321        Sensor occupancy = block.getOccupancySensor();
7322        if (occupancy == null) {
7323            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7324                    Bundle.getMessage("InfoMessage4",
7325                            new Object[]{block.getUserName()}),
7326                    Bundle.getMessage("MessageTitle"),
7327                    JmriJOptionPane.INFORMATION_MESSAGE);
7328            return;
7329        }
7330        String signalHeadName = b_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7331        if (signalHeadName == null) {
7332            signalHeadName = "";
7333        }
7334        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7335                signalHeadName, setSignalsAt3WayTurnoutFrame);
7336        if ((nextHead == null) && (!reachedEndBumper())) {
7337            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7338                    Bundle.getMessage("InfoMessage5",
7339                            new Object[]{block.getUserName()}),
7340                    Bundle.getMessage("MessageTitle"),
7341                    JmriJOptionPane.INFORMATION_MESSAGE);
7342            return;
7343        }
7344        if (!initializeBlockBossLogic(signalHeadName)) {
7345            return;
7346        }
7347        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7348        logic.setTurnout(turnoutA.getDisplayName());
7349        logic.setSensor1(occupancy.getDisplayName());
7350        if (nextHead != null) {
7351            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7352        }
7353        if (auxSignal != null) {
7354            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7355        }
7356        if (!layoutTurnoutA.isMainlineC()) {
7357            logic.setLimitSpeed2(true);
7358        }
7359        finalizeBlockBossLogic();
7360    }   //set3WayLogicDivergingA
7361
7362    private void set3WayLogicContinuing() {
7363        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectA();
7364        if (track == null) {
7365            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7366                    Bundle.getMessage("InfoMessage7"),
7367                    Bundle.getMessage("MessageTitle"),
7368                    JmriJOptionPane.INFORMATION_MESSAGE);
7369            return;
7370        }
7371        LayoutBlock block = track.getLayoutBlock();
7372        if (block == null) {
7373            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7374                    Bundle.getMessage("InfoMessage6"),
7375                    Bundle.getMessage("MessageTitle"),
7376                    JmriJOptionPane.INFORMATION_MESSAGE);
7377            return;
7378        }
7379        Sensor occupancy = block.getOccupancySensor();
7380        if (occupancy == null) {
7381            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7382                    Bundle.getMessage("InfoMessage4",
7383                            new Object[]{block.getUserName()}),
7384                    Bundle.getMessage("MessageTitle"),
7385                    JmriJOptionPane.INFORMATION_MESSAGE);
7386            return;
7387        }
7388        String signalHeadName = c_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7389        if (signalHeadName == null) {
7390            signalHeadName = "";
7391        }
7392        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7393                signalHeadName, setSignalsAt3WayTurnoutFrame);
7394        if ((nextHead == null) && (!reachedEndBumper())) {
7395            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7396                    Bundle.getMessage("InfoMessage5",
7397                            new Object[]{block.getUserName()}),
7398                    Bundle.getMessage("MessageTitle"),
7399                    JmriJOptionPane.INFORMATION_MESSAGE);
7400            return;
7401        }
7402        if (!initializeBlockBossLogic(signalHeadName)) {
7403            return;
7404        }
7405        logic.setMode(BlockBossLogic.TRAILINGMAIN);
7406        logic.setTurnout(turnoutB.getDisplayName());
7407        logic.setSensor1(occupancy.getDisplayName());
7408        if (nextHead != null) {
7409            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7410        }
7411        if (auxSignal != null) {
7412            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7413        }
7414        String nearSensorName = setupNearLogix(layoutTurnoutA, true, c3WayHead);
7415        addNearSensorToLogic(nearSensorName);
7416        if (!layoutTurnoutB.isMainlineB()) {
7417            logic.setLimitSpeed2(true);
7418        }
7419        finalizeBlockBossLogic();
7420    }   //set3WayLogicContinuing
7421
7422    private void set3WayLogicDivergingB() {
7423        TrackSegment track = (TrackSegment) layoutTurnoutA.getConnectA();
7424        if (track == null) {
7425            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7426                    Bundle.getMessage("InfoMessage7"),
7427                    Bundle.getMessage("MessageTitle"),
7428                    JmriJOptionPane.INFORMATION_MESSAGE);
7429            return;
7430        }
7431        LayoutBlock block = track.getLayoutBlock();
7432        if (block == null) {
7433            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7434                    Bundle.getMessage("InfoMessage6"),
7435                    Bundle.getMessage("MessageTitle"),
7436                    JmriJOptionPane.INFORMATION_MESSAGE);
7437            return;
7438        }
7439        Sensor occupancy = block.getOccupancySensor();
7440        if (occupancy == null) {
7441            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7442                    Bundle.getMessage("InfoMessage4",
7443                            new Object[]{block.getUserName()}),
7444                    Bundle.getMessage("MessageTitle"),
7445                    JmriJOptionPane.INFORMATION_MESSAGE);
7446            return;
7447        }
7448        String signalHeadName = d_3WaySignalHeadComboBox.getSelectedItemDisplayName();
7449        if (signalHeadName == null) {
7450            signalHeadName = "";
7451        }
7452        SignalHead nextHead = getNextSignalFromObject(track, layoutTurnoutA,
7453                signalHeadName, setSignalsAt3WayTurnoutFrame);
7454        if ((nextHead == null) && (!reachedEndBumper())) {
7455            JmriJOptionPane.showMessageDialog(setSignalsAt3WayTurnoutFrame,
7456                    Bundle.getMessage("InfoMessage5",
7457                            new Object[]{block.getUserName()}),
7458                    Bundle.getMessage("MessageTitle"),
7459                    JmriJOptionPane.INFORMATION_MESSAGE);
7460            return;
7461        }
7462        if (!initializeBlockBossLogic(signalHeadName)) {
7463            return;
7464        }
7465        logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
7466        logic.setTurnout(turnoutB.getDisplayName());
7467        logic.setSensor1(occupancy.getDisplayName());
7468        if (nextHead != null) {
7469            logic.setWatchedSignal1(nextHead.getDisplayName(), false);
7470        }
7471        if (auxSignal != null) {
7472            logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
7473        }
7474        String nearSensorName = setupNearLogix(layoutTurnoutA, true, d3WayHead);
7475        addNearSensorToLogic(nearSensorName);
7476        if (!layoutTurnoutB.isMainlineC()) {
7477            logic.setLimitSpeed2(true);
7478        }
7479        finalizeBlockBossLogic();
7480    }   //set3WayLogicDivergingB
7481
7482    /*===========================*\
7483    |* setSensorsAtBlockBoundary *|
7484    \*===========================*/
7485    //
7486    //The following is for placement of sensors and signal masts at points around the layout
7487    //
7488    //This section deals with assigning a sensor to a specific boundary point
7489    BeanDetails<Sensor> westBoundSensor;
7490    BeanDetails<Sensor> eastBoundSensor;
7491
7492    private JmriJFrame setSensorsAtBlockBoundaryFrame = null;
7493    private boolean setSensorsAtBlockBoundaryOpenFlag = false;
7494    private boolean setSensorsAtBlockBoundaryFromMenuFlag = false;
7495
7496    private JButton getAnchorSavedSensors = null;
7497    private JButton changeSensorAtBoundaryIcon = null;
7498    private JButton setSensorsAtBlockBoundaryDone = null;
7499    private JButton setSensorsAtBlockBoundaryCancel = null;
7500
7501    private JFrame sensorFrame = null;
7502    private MultiIconEditor sensorIconEditor = null;
7503
7504    JPanel sensorBlockPanel = new JPanel(new FlowLayout());
7505
7506    public void setSensorsAtBlockBoundaryFromMenu(@Nonnull PositionablePoint p,
7507            @Nonnull MultiIconEditor theEditor,
7508            @Nonnull JFrame theFrame) {
7509        boundary = p;
7510        block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
7511        if (boundary.getConnect2() == null) {
7512            block2IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
7513        } else {
7514            block2IDComboBox.setSelectedItem(boundary.getConnect2().getLayoutBlock().getBlock());
7515        }
7516        setSensorsAtBlockBoundaryFromMenuFlag = true;
7517        setSensorsAtBlockBoundary(theEditor, theFrame);
7518        setSensorsAtBlockBoundaryFromMenuFlag = false;
7519    }
7520
7521    //TODO: Add to Tools menu?
7522    public void setSensorsAtBlockBoundary(@Nonnull MultiIconEditor theEditor,
7523            @Nonnull JFrame theFrame) {
7524        sensorIconEditor = theEditor;
7525        sensorFrame = theFrame;
7526
7527        //Initialize if needed
7528        if (setSensorsAtBlockBoundaryFrame == null) {
7529            setSensorsAtBlockBoundaryOpenFlag = false;
7530
7531            westBoundSensor = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
7532            eastBoundSensor = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
7533
7534            setSensorsAtBlockBoundaryFrame = new JmriJFrame(Bundle.getMessage("SensorsAtBoundary"), false, true);
7535            oneFrameToRuleThemAll(setSensorsAtBlockBoundaryFrame);
7536            setSensorsAtBlockBoundaryFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
7537//         setSensorsAtBlockBoundaryFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtBoundary", true);
7538            setSensorsAtBlockBoundaryFrame.setLocation(70, 30);
7539            Container theContentPane = setSensorsAtBlockBoundaryFrame.getContentPane();
7540            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
7541
7542            JPanel header = new JPanel();
7543            header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS));
7544
7545            JPanel panel11 = new JPanel(new FlowLayout());
7546            block1NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
7547                    Bundle.getMessage("BeanNameBlock") + " 1 - "
7548                    + Bundle.getMessage("Name")));
7549            panel11.add(block1NameLabel);
7550
7551            panel11.add(block1IDComboBox);
7552            block1IDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
7553            header.add(panel11);
7554
7555            JPanel panel12 = new JPanel(new FlowLayout());
7556            block2NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
7557                    Bundle.getMessage("BeanNameBlock") + " 2 - "
7558                    + Bundle.getMessage("Name")));
7559            panel12.add(block2NameLabel);
7560
7561            panel12.add(block2IDComboBox);
7562            block2IDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
7563            header.add(panel12);
7564
7565            header.add(new JSeparator(JSeparator.HORIZONTAL));
7566            theContentPane.add(header);
7567
7568            JPanel panel2 = new JPanel(new FlowLayout());
7569            JLabel shTitle = new JLabel(Bundle.getMessage("Sensors"));
7570            panel2.add(shTitle);
7571            panel2.add(new JLabel("   "));
7572            panel2.add(getAnchorSavedSensors = new JButton(Bundle.getMessage("GetSaved")));
7573            getAnchorSavedSensors.addActionListener(this::getSavedAnchorSensors);
7574            getAnchorSavedSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
7575            theContentPane.add(panel2);
7576
7577            sensorBlockPanel.setLayout(new GridLayout(0, 1));
7578            theContentPane.add(sensorBlockPanel);
7579
7580            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
7581
7582            JPanel panel6 = new JPanel(new FlowLayout());
7583            panel6.add(changeSensorAtBoundaryIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
7584            changeSensorAtBoundaryIcon.addActionListener((ActionEvent e) -> sensorFrame.setVisible(true));
7585            changeSensorAtBoundaryIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
7586            panel6.add(new JLabel("   "));
7587            panel6.add(setSensorsAtBlockBoundaryDone = new JButton(Bundle.getMessage("ButtonDone")));
7588            setSensorsAtBlockBoundaryDone.addActionListener(this::setSensorsAtBlockBoundaryDonePressed);
7589            setSensorsAtBlockBoundaryDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
7590
7591            panel6.add(setSensorsAtBlockBoundaryCancel = new JButton(Bundle.getMessage("ButtonCancel")));
7592            setSensorsAtBlockBoundaryCancel.addActionListener(this::setSensorsAtBlockBoundaryCancelPressed);
7593            setSensorsAtBlockBoundaryCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
7594            theContentPane.add(panel6, BorderLayout.SOUTH);
7595
7596//make this button the default button (return or enter activates)
7597            JRootPane rootPane = SwingUtilities.getRootPane(setSensorsAtBlockBoundaryDone);
7598            if (rootPane != null) {
7599                rootPane.setDefaultButton(setSensorsAtBlockBoundaryDone);
7600            }
7601
7602            setSensorsAtBlockBoundaryFrame.addWindowListener(new WindowAdapter() {
7603                @Override
7604                public void windowClosing(WindowEvent e) {
7605                    setSensorsAtBlockBoundaryCancelPressed(null);
7606                }
7607            });
7608        }
7609
7610        sensorBlockPanel.removeAll();
7611
7612        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
7613            eastBoundSensor.setBoundaryTitle(Bundle.getMessage("East/SouthBound"));
7614            if ((setSensorsAtBlockBoundaryFromMenuFlag) && (boundary.getType() == PositionablePoint.PointType.ANCHOR)) {
7615                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7616                    eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7617                } else {
7618                    eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7619                }
7620            }
7621            eastBoundSensor.getDetailsPanel().setBackground(new Color(255, 255, 200));
7622            sensorBlockPanel.add(eastBoundSensor.getDetailsPanel());
7623
7624            westBoundSensor.setBoundaryTitle(Bundle.getMessage("West/NorthBound"));
7625            if (setSensorsAtBlockBoundaryFromMenuFlag) {
7626                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7627                    westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7628                } else {
7629                    westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7630                }
7631            }
7632            westBoundSensor.getDetailsPanel().setBackground(new Color(200, 255, 255));
7633            sensorBlockPanel.add(westBoundSensor.getDetailsPanel());
7634        } else {
7635            if (setSensorsAtBlockBoundaryFromMenuFlag) {
7636                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7637                    eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7638                    eastBoundSensor.getDetailsPanel().setBackground(new Color(200, 255, 255));
7639                    sensorBlockPanel.add(eastBoundSensor.getDetailsPanel());
7640                } else {
7641                    westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7642                    westBoundSensor.getDetailsPanel().setBackground(new Color(255, 255, 200));
7643                    sensorBlockPanel.add(westBoundSensor.getDetailsPanel());
7644                }
7645            }
7646        }
7647
7648        block1IDComboBox.setVisible(!setSensorsAtBlockBoundaryFromMenuFlag);
7649        block2IDComboBox.setVisible(!setSensorsAtBlockBoundaryFromMenuFlag);
7650
7651        if (setSensorsAtBlockBoundaryFromMenuFlag) {
7652            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
7653                    Bundle.getMessage("BeanNameBlock") + " 1 "
7654                    + Bundle.getMessage("Name"))
7655                    + " " + boundary.getConnect1().getLayoutBlock().getId());
7656            if (boundary.getConnect2() != null) {
7657                block2NameLabel.setText(Bundle.getMessage("MakeLabel",
7658                        Bundle.getMessage("BeanNameBlock") + " 2 "
7659                        + Bundle.getMessage("Name"))
7660                        + " " + boundary.getConnect2().getLayoutBlock().getId());
7661            }
7662            getSavedAnchorSensors(null);
7663        } else {
7664            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
7665                    Bundle.getMessage("Name") + " 1 "
7666                    + Bundle.getMessage("Name")));
7667            block2NameLabel.setText(Bundle.getMessage("MakeLabel",
7668                    Bundle.getMessage("Name") + " 2  "
7669                    + Bundle.getMessage("Name")));
7670        }
7671        //boundary should never be null... however, just in case...
7672        boolean enable = ((boundary != null) && (boundary.getType() != PositionablePoint.PointType.END_BUMPER));
7673        block2NameLabel.setVisible(enable);
7674
7675        if (!setSensorsAtBlockBoundaryOpenFlag) {
7676            setSensorsAtBlockBoundaryFrame.setPreferredSize(null);
7677            setSensorsAtBlockBoundaryFrame.pack();
7678            setSensorsAtBlockBoundaryOpenFlag = true;
7679        }
7680        setSensorsAtBlockBoundaryFrame.setVisible(true);
7681    }   //setSensorsAtBlockBoundary
7682
7683    /**
7684     * Returns the Sensor corresponding to an entry field in the specified
7685     * dialog.
7686     * <p>
7687     * This also takes care of UpperCase and trimming of leading and
7688     * trailing blanks.
7689     * If entry is required, and no entry is present, an error message is sent.
7690     * An error message also results if a sensor head with the
7691     * entered name is not found in the SensorTable.
7692     * @param sensorName sensor name.
7693     * @param requireEntry true if mandatory field, else false.
7694     * @param frame the main frame.
7695     * @return sensor, may be null.
7696     */
7697    @CheckReturnValue
7698    public Sensor getSensorFromEntry(@CheckForNull String sensorName,
7699            boolean requireEntry,
7700            @Nonnull JmriJFrame frame) {
7701        if ((sensorName == null) || sensorName.isEmpty()) {
7702            if (requireEntry) {
7703                JmriJOptionPane.showMessageDialog(frame, Bundle.getMessage("SensorsError5"),
7704                        Bundle.getMessage("ErrorTitle"),
7705                        JmriJOptionPane.ERROR_MESSAGE);
7706            }
7707            return null;
7708        }
7709        Sensor head = InstanceManager.sensorManagerInstance().getSensor(sensorName);
7710        if (head == null) {
7711            JmriJOptionPane.showMessageDialog(frame,
7712                    Bundle.getMessage("SensorsError4",
7713                            new Object[]{sensorName}), Bundle.getMessage("ErrorTitle"),
7714                    JmriJOptionPane.ERROR_MESSAGE);
7715            return null;
7716        }
7717        return (head);
7718    }
7719
7720    @CheckReturnValue
7721    public SensorIcon getSensorIcon(@Nonnull String sensorName) {
7722        SensorIcon l = new SensorIcon(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif",
7723                "resources/icons/smallschematics/tracksegments/circuit-error.gif"), layoutEditor);
7724        l.setIcon("SensorStateActive", sensorIconEditor.getIcon(0));
7725        l.setIcon("SensorStateInactive", sensorIconEditor.getIcon(1));
7726        l.setIcon("BeanStateInconsistent", sensorIconEditor.getIcon(2));
7727        l.setIcon("BeanStateUnknown", sensorIconEditor.getIcon(3));
7728        l.setSensor(sensorName);
7729        return l;
7730    }
7731
7732    /**
7733     * Returns true if the specified Sensor is assigned to an object on the
7734     * panel, regardless of whether an icon is displayed or not. With sensors we
7735     * NO LONGER (4.11.2) allow the same sensor to be allocated in both
7736     * directions.
7737     *
7738     * @param sensor The sensor to be checked.
7739     * @return true if the sensor is currently assigned someplace.
7740     */
7741    public boolean isSensorAssignedAnywhere(@Nonnull Sensor sensor) {
7742        boolean result = false;
7743
7744        //check positionable points
7745        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
7746            if (po.getEastBoundSensor() == sensor) {
7747                result = true;
7748                break;
7749            }
7750            if (po.getWestBoundSensor() == sensor) {
7751                result = true;
7752                break;
7753            }
7754        }
7755        if (!result) {
7756            //check turnouts and slips
7757            for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
7758                if (whereIsSensorAssigned(sensor, to) != LayoutTurnout.Geometry.NONE) {
7759                    result = true;
7760                    break;
7761                }
7762            }
7763        }
7764        if (!result) {
7765            //check level crossings
7766            for (LevelXing x : layoutEditor.getLevelXings()) {
7767                if ((x.getSensorA() != null) && x.getSensorA() == sensor) {
7768                    result = true;
7769                    break;
7770                }
7771                if ((x.getSensorB() != null) && x.getSensorB() == sensor) {
7772                    result = true;
7773                    break;
7774                }
7775                if ((x.getSensorC() != null) && x.getSensorC() == sensor) {
7776                    result = true;
7777                    break;
7778                }
7779                if ((x.getSensorD() != null) && x.getSensorD() == sensor) {
7780                    result = true;
7781                    break;
7782                }
7783            }
7784        }
7785
7786        return result;
7787    }   //isSensorAssignedAnywhere
7788
7789    private LayoutTurnout.Geometry whereIsSensorAssigned(Sensor sensor, LayoutTurnout lTurnout) {
7790        LayoutTurnout.Geometry result = LayoutTurnout.Geometry.NONE;
7791
7792        if (sensor != null && lTurnout != null) {
7793            String sName = sensor.getSystemName();
7794            String uName = sensor.getUserName();
7795
7796            String name = lTurnout.getSensorAName();
7797            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7798                return LayoutTurnout.Geometry.POINTA1;
7799            }
7800            name = lTurnout.getSensorBName();
7801            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7802                return LayoutTurnout.Geometry.POINTA2;
7803            }
7804            name = lTurnout.getSensorCName();
7805            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7806                return LayoutTurnout.Geometry.POINTA3;
7807            }
7808            name = lTurnout.getSensorDName();
7809            if (!name.isEmpty() && name.equals(uName) || name.equals(sName)) {
7810                return LayoutTurnout.Geometry.POINTB1;
7811            }
7812        }
7813        return result;
7814    }
7815
7816    /**
7817     * Display an error dialog.
7818     *
7819     * @param sensor The sensor that is already assigned.
7820     */
7821    void sensorAssignedElseWhere(@Nonnull Sensor sensor) {
7822        JmriJOptionPane.showMessageDialog(setSensorsAtBlockBoundaryFrame,
7823                Bundle.getMessage("SensorsError6", // NOI18N
7824                        new Object[]{sensor.getDisplayName()}),
7825                Bundle.getMessage("ErrorTitle"),
7826                JmriJOptionPane.ERROR_MESSAGE);  // NOI18N
7827    }
7828
7829    /**
7830     * Removes the assignment of the specified Sensor to either a turnout, a
7831     * positionable point, or a level crossing wherever it is assigned. Removes
7832     * any NX Pairs that use the sensor.
7833     * <p>
7834     * If the NX deletes fail due to Conditional references or user deny, the
7835     * assignment is not deleted. No additional notification is necessary since
7836     * they have already been notified or made a choice to not continue.
7837     *
7838     * @param sensor The sensor to be removed.
7839     * @return true if the sensor has been removed.
7840     */
7841    public boolean removeSensorAssignment(@Nonnull Sensor sensor) {
7842        log.trace("Remove sensor assignment at block boundary for '{}'", sensor.getDisplayName());  // NOI18N
7843        if (!InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).deleteNxPair(sensor)) {
7844            log.trace("Removal of NX pairs for sensor '{}' failed", sensor.getDisplayName());  // NOI18N
7845            return false;
7846        }
7847        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
7848            if (po.getEastBoundSensor() == sensor) {
7849                po.setEastBoundSensor(null);
7850            }
7851            if (po.getWestBoundSensor() == sensor) {
7852                po.setWestBoundSensor(null);
7853            }
7854        }
7855
7856        for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
7857            if (to.getSensorA() == sensor) {
7858                to.setSensorA(null);
7859            }
7860            if (to.getSensorB() == sensor) {
7861                to.setSensorB(null);
7862            }
7863            if (to.getSensorC() == sensor) {
7864                to.setSensorC(null);
7865            }
7866            if (to.getSensorD() == sensor) {
7867                to.setSensorD(null);
7868            }
7869        }
7870
7871        for (LevelXing x : layoutEditor.getLevelXings()) {
7872            if (x.getSensorA() == sensor) {
7873                x.setSensorAName(null);
7874            }
7875            if (x.getSensorB() == sensor) {
7876                x.setSensorBName(null);
7877            }
7878            if (x.getSensorC() == sensor) {
7879                x.setSensorCName(null);
7880            }
7881            if (x.getSensorD() == sensor) {
7882                x.setSensorDName(null);
7883            }
7884        }
7885
7886        return true;
7887    }   //removeSensorAssignment
7888
7889    /**
7890     * Removes the Sensor icon from the panel and from assignment to any
7891     * turnout, positionable point, or level crossing.
7892     *
7893     * @param sensor The sensor whose icon and references are to be removed.
7894     * @return true if the removal was successful.
7895     */
7896    public boolean removeSensorFromPanel(@Nonnull Sensor sensor) {
7897        log.trace("Remove sensor icon and assignment for '{}'", sensor.getDisplayName());  // NOI18N
7898        if (!removeSensorAssignment(sensor)) {
7899            return false;
7900        }
7901
7902        SensorIcon h = null;
7903        int index = -1;
7904        for (int i = 0; (i < layoutEditor.getSensorList().size()) && (index == -1); i++) {
7905            h = layoutEditor.getSensorList().get(i);
7906            if (h.getSensor() == sensor) {
7907                index = i;
7908            }
7909        }
7910        if ((h != null) && (index != -1)) {
7911            layoutEditor.getSensorList().remove(index);
7912            h.remove();
7913            h.dispose();
7914            needRedraw = true;
7915        }
7916        return true;
7917    }
7918
7919    private void getSavedAnchorSensors(ActionEvent a) {
7920        if (!getSimpleBlockInformation()) {
7921            return;
7922        }
7923        eastBoundSensor.setTextField(boundary.getEastBoundSensorName());
7924        westBoundSensor.setTextField(boundary.getWestBoundSensorName());
7925
7926        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
7927            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7928                eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7929            } else {
7930                eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7931            }
7932            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7933                westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7934            } else {
7935                westBoundSensor.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
7936            }
7937        } else {
7938            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
7939                westBoundSensor.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7940            } else {
7941                eastBoundSensor.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
7942            }
7943        }
7944
7945        setSensorsAtBlockBoundaryFrame.setPreferredSize(null);
7946        setSensorsAtBlockBoundaryFrame.pack();
7947    }
7948
7949    private void setSensorsAtBlockBoundaryCancelPressed(ActionEvent a) {
7950        setSensorsAtBlockBoundaryOpenFlag = false;
7951        setSensorsAtBlockBoundaryFrame.setVisible(false);
7952    }
7953
7954    private void setSensorsAtBlockBoundaryDonePressed(ActionEvent a) {
7955        log.trace("setSensorsAtBlockBoundaryDonePressed");  // NOI18N
7956        if (!getSimpleBlockInformation()) {
7957            return;
7958        }
7959
7960        Sensor eastSensor = getSensorFromEntry(eastBoundSensor.getText(), false, setSensorsAtBlockBoundaryFrame);
7961        Sensor westSensor = getSensorFromEntry(westBoundSensor.getText(), false, setSensorsAtBlockBoundaryFrame);
7962        Sensor currEastSensor = InstanceManager.sensorManagerInstance().getSensor(boundary.getEastBoundSensorName());
7963        Sensor currWestSensor = InstanceManager.sensorManagerInstance().getSensor(boundary.getWestBoundSensorName());
7964
7965        if (log.isTraceEnabled()) {
7966            log.trace("current sensors: east = {}, west = {}", // NOI18N
7967                    (currEastSensor == null) ? "- none- " : currEastSensor.getDisplayName(), // NOI18N
7968                    (currWestSensor == null) ? "- none- " : currWestSensor.getDisplayName());  // NOI18N
7969            log.trace("new sensors: east = {}, west = {}", // NOI18N
7970                    (eastSensor == null) ? "- none- " : eastSensor.getDisplayName(), // NOI18N
7971                    (westSensor == null) ? "- none- " : westSensor.getDisplayName());  // NOI18N
7972        }
7973
7974        if (eastSensor == null) {
7975            if (currEastSensor != null && removeSensorFromPanel(currEastSensor)) {
7976                boundary.setEastBoundSensor(null);
7977            }
7978        } else if (eastBoundSensor != null) {
7979            setBoundarySensor(eastSensor, currEastSensor, eastBoundSensor, "East");  // NOI18N
7980        }
7981
7982        if (westSensor == null) {
7983            if (currWestSensor != null && removeSensorFromPanel(currWestSensor)) {
7984                boundary.setWestBoundSensor(null);
7985            }
7986        } else if (westBoundSensor != null) {
7987            setBoundarySensor(westSensor, currWestSensor, westBoundSensor, "West");  // NOI18N
7988        }
7989
7990        setSensorsAtBlockBoundaryOpenFlag = false;
7991        setSensorsAtBlockBoundaryFrame.setVisible(false);
7992        if (needRedraw) {
7993            layoutEditor.redrawPanel();
7994            needRedraw = false;
7995            layoutEditor.setDirty();
7996        }
7997    }
7998
7999    /**
8000     * Attached a sensor to the block boundary positional point.
8001     *
8002     * @since 4.11.2
8003     * @param newSensor  The sensor that is being added.
8004     * @param currSensor The sensor that might already be there, otherwise null.
8005     * @param beanDetail The BeanDetails object that contains the supporting
8006     *                   data.
8007     * @param direction  The direction, East or West.
8008     */
8009    void setBoundarySensor(Sensor newSensor, Sensor currSensor,
8010            BeanDetails<Sensor> beanDetail, String direction) {
8011        if (currSensor == null) {
8012            if (!isSensorAssignedAnywhere(newSensor)) {
8013                log.trace("Add sensor '{}'", newSensor.getDisplayName());  // NOI18N
8014                if (direction.equals("West")) {  // NOI18N
8015                    boundary.setWestBoundSensor(beanDetail.getText());
8016                } else {
8017                    boundary.setEastBoundSensor(beanDetail.getText());
8018                }
8019                if (beanDetail.addToPanel()) {
8020                    log.trace("Add icon for sensor '{}'", newSensor.getDisplayName());  // NOI18N
8021                    if (direction.equals("West")) {  // NOI18N
8022                        placeWestBoundIcon(getSensorIcon(beanDetail.getText()),
8023                                beanDetail.isRightSelected(), 0.0);
8024                    } else {
8025                        placeEastBoundIcon(getSensorIcon(beanDetail.getText()),
8026                                beanDetail.isRightSelected(), 0.0);
8027                    }
8028                    needRedraw = true;
8029                }
8030            } else {
8031                sensorAssignedElseWhere(newSensor);
8032            }
8033        } else if (currSensor == newSensor) {
8034            if (beanDetail.addToPanel()) {
8035                if (!isSensorOnPanel(newSensor)) {
8036                    log.trace("Add icon for existing sensor '{}'", newSensor.getDisplayName());  // NOI18N
8037                    if (direction.equals("West")) {  // NOI18N
8038                        placeWestBoundIcon(getSensorIcon(beanDetail.getText()),
8039                                beanDetail.isRightSelected(), 0.0);
8040                    } else {
8041                        placeEastBoundIcon(getSensorIcon(beanDetail.getText()),
8042                                beanDetail.isRightSelected(), 0.0);
8043                    }
8044                    needRedraw = true;
8045                }
8046            }
8047        } else {
8048            if (!isSensorAssignedAnywhere(newSensor)) {
8049                if (removeSensorFromPanel(currSensor)) {
8050                    log.trace("Replace sensor '{}' with sensor '{}'", // NOI18N
8051                            currSensor.getDisplayName(), newSensor.getDisplayName());
8052                    if (direction.equals("West")) {  // NOI18N
8053                        boundary.setWestBoundSensor(beanDetail.getText());
8054                    } else {
8055                        boundary.setEastBoundSensor(beanDetail.getText());
8056                    }
8057                    if (beanDetail.addToPanel()) {
8058                        log.trace("Add icon for replacement sensor '{}'", // NOI18N
8059                                newSensor.getDisplayName());
8060                        if (direction.equals("West")) {  // NOI18N
8061                            placeWestBoundIcon(getSensorIcon(beanDetail.getText()),
8062                                    beanDetail.isRightSelected(), 0.0);
8063                        } else {
8064                            placeEastBoundIcon(getSensorIcon(beanDetail.getText()),
8065                                    beanDetail.isRightSelected(), 0.0);
8066                        }
8067                        needRedraw = true;
8068                    }
8069                }
8070            } else {
8071                sensorAssignedElseWhere(newSensor);
8072            }
8073        }
8074    }
8075
8076    public boolean isSensorOnPanel(@Nonnull Sensor sensor) {
8077        for (SensorIcon s : layoutEditor.getSensorList()) {
8078            if (s.getSensor() == sensor) {
8079                return true;
8080            }
8081        }
8082        return false;
8083    }
8084
8085    /*===============================*\
8086    |* setSignalMastsAtBlockBoundary *|
8087    \*===============================*/
8088    private JmriJFrame setSignalMastsAtBlockBoundaryFrame = null;
8089    private boolean setSignalMastsAtBlockBoundaryOpenFlag = false;
8090    private boolean setSignalMastsAtBlockBoundaryFromMenuFlag = false;
8091
8092    private JButton getAnchorSavedSignalMasts = null;
8093    private JButton setSignalMastsAtBlockBoundaryDone = null;
8094    private JButton setSignalMastsAtBlockBoundaryCancel = null;
8095
8096    BeanDetails<SignalMast> eastSignalMast;
8097    BeanDetails<SignalMast> westSignalMast;
8098
8099    JPanel signalMastBlockPanel = new JPanel(new FlowLayout());
8100
8101    public void setSignalMastsAtBlockBoundaryFromMenu(
8102            @Nonnull PositionablePoint p) {
8103        boundary = p;
8104        block1IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
8105        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
8106            block2IDComboBox.setSelectedItem(boundary.getConnect2().getLayoutBlock().getBlock());
8107        } else {
8108            block2IDComboBox.setSelectedItem(boundary.getConnect1().getLayoutBlock().getBlock());
8109        }
8110        setSignalMastsAtBlockBoundaryFromMenuFlag = true;
8111        setSignalMastsAtBlockBoundary();
8112        setSignalMastsAtBlockBoundaryFromMenuFlag = false;
8113    }
8114
8115    //TODO: Add to Tools menu?
8116    public void setSignalMastsAtBlockBoundary() {
8117
8118        //Initialize if needed
8119        if (setSignalMastsAtBlockBoundaryFrame == null) {
8120            setSignalMastsAtBlockBoundaryOpenFlag = false;
8121
8122            eastSignalMast = new BeanDetails<>("SignalMast", // NOI18N
8123                    InstanceManager.getDefault(SignalMastManager.class));
8124            westSignalMast = new BeanDetails<>("SignalMast", // NOI18N
8125                    InstanceManager.getDefault(SignalMastManager.class));
8126
8127            setSignalMastsAtBlockBoundaryFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtBoundary"), false, true);
8128            oneFrameToRuleThemAll(setSignalMastsAtBlockBoundaryFrame);
8129            setSignalMastsAtBlockBoundaryFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
8130            //setSignalMastsAtBlockBoundaryFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalMastsAtBoundary", true);
8131            setSignalMastsAtBlockBoundaryFrame.setLocation(70, 30);
8132            Container theContentPane = setSignalMastsAtBlockBoundaryFrame.getContentPane();
8133            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
8134
8135            JPanel header = new JPanel();
8136            header.setLayout(new BoxLayout(header, BoxLayout.Y_AXIS));
8137
8138            //Create the block 1 label and combo box
8139            JPanel panel11 = new JPanel(new FlowLayout());
8140            block1NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
8141                    Bundle.getMessage("BeanNameBlock") + " 1 "
8142                    + Bundle.getMessage("Name")));
8143            panel11.add(block1NameLabel);
8144            panel11.add(block1IDComboBox);
8145            block1IDComboBox.setToolTipText(Bundle.getMessage("SignalMastsBlockNameHint"));
8146            header.add(panel11);
8147
8148            //Create the block 2 label and combo box, visibility will be controlled later
8149            block2NameLabel = new JLabel(Bundle.getMessage("MakeLabel",
8150                    Bundle.getMessage("BeanNameBlock") + " 2 "
8151                    + Bundle.getMessage("Name")));
8152            block2IDComboBox.setToolTipText(Bundle.getMessage("SignalMastsBlockNameHint"));
8153
8154            JPanel panel12 = new JPanel(new FlowLayout());
8155            panel12.add(block2NameLabel);
8156            panel12.add(block2IDComboBox);
8157            header.add(panel12);
8158
8159            header.add(new JSeparator(JSeparator.HORIZONTAL));
8160            theContentPane.add(header);
8161
8162            JPanel panel2 = new JPanel(new FlowLayout());
8163            JLabel shTitle = new JLabel(Bundle.getMessage("SignalMasts"));
8164            panel2.add(shTitle);
8165            panel2.add(new JLabel("   "));
8166            panel2.add(getAnchorSavedSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
8167            getAnchorSavedSignalMasts.addActionListener(this::getSavedAnchorSignalMasts);
8168            getAnchorSavedSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
8169            theContentPane.add(panel2);
8170
8171            signalMastBlockPanel.setLayout(new GridLayout(0, 1));
8172            theContentPane.add(signalMastBlockPanel);
8173
8174            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
8175
8176            JPanel panel6 = new JPanel(new FlowLayout());
8177            panel6.add(setSignalMastsAtBlockBoundaryDone = new JButton(Bundle.getMessage("ButtonDone")));
8178            setSignalMastsAtBlockBoundaryDone.addActionListener(this::setSignalMastsAtBlockBoundaryDonePressed);
8179            setSignalMastsAtBlockBoundaryDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
8180
8181            panel6.add(setSignalMastsAtBlockBoundaryCancel = new JButton(Bundle.getMessage("ButtonCancel")));
8182            setSignalMastsAtBlockBoundaryCancel.addActionListener(this::setSignalMastsAtBlockBoundaryCancelPressed);
8183            setSignalMastsAtBlockBoundaryCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
8184            theContentPane.add(panel6);
8185
8186            //make this button the default button (return or enter activates)
8187            JRootPane rootPane = SwingUtilities.getRootPane(setSignalMastsAtBlockBoundaryDone);
8188            if (rootPane != null) {
8189                rootPane.setDefaultButton(setSignalMastsAtBlockBoundaryDone);
8190            }
8191
8192            setSignalMastsAtBlockBoundaryFrame.addWindowListener(new WindowAdapter() {
8193                @Override
8194                public void windowClosing(WindowEvent e) {
8195                    setSignalMastsAtBlockBoundaryCancelPressed(null);
8196                }
8197            });
8198        }
8199
8200        eastSignalMast.getCombo().setExcludedItems(new HashSet<>());
8201        westSignalMast.getCombo().setExcludedItems(new HashSet<>());
8202        signalMastBlockPanel.removeAll();
8203
8204        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {   //Anchor points and Edge Connectors
8205            eastSignalMast.setBoundaryTitle(Bundle.getMessage("East/SouthBound"));
8206            if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8207                eastSignalMast.setBoundaryTitle(Bundle.getMessage("West/NorthBound"));
8208            }
8209            if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8210                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8211                    eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8212                } else {
8213                    eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8214                }
8215            }
8216            eastSignalMast.getDetailsPanel().setBackground(new Color(255, 255, 200));
8217            signalMastBlockPanel.add(eastSignalMast.getDetailsPanel());
8218
8219            westSignalMast.setBoundaryTitle(Bundle.getMessage("West/NorthBound"));
8220            if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8221                westSignalMast.setBoundaryTitle(Bundle.getMessage("East/SouthBound"));
8222            }
8223            if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8224                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8225                    westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8226                } else {
8227                    westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8228                }
8229            }
8230            westSignalMast.getDetailsPanel().setBackground(new Color(200, 255, 255));
8231            signalMastBlockPanel.add(westSignalMast.getDetailsPanel());
8232        } else {    //End Bumper
8233            if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8234                if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8235                    eastSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8236                    eastSignalMast.getDetailsPanel().setBackground(new Color(200, 255, 255));
8237                    signalMastBlockPanel.add(eastSignalMast.getDetailsPanel());
8238                } else {
8239                    westSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8240                    westSignalMast.getDetailsPanel().setBackground(new Color(255, 255, 200));
8241                    signalMastBlockPanel.add(westSignalMast.getDetailsPanel());
8242                }
8243            }
8244        }
8245        block1IDComboBox.setVisible(!setSignalMastsAtBlockBoundaryFromMenuFlag);
8246        block2IDComboBox.setVisible(!setSignalMastsAtBlockBoundaryFromMenuFlag);
8247
8248        if (setSignalMastsAtBlockBoundaryFromMenuFlag) {
8249            block1NameLabel.setText(Bundle.getMessage("MakeLabel",
8250                    Bundle.getMessage("BeanNameBlock") + " 1 "
8251                    + Bundle.getMessage("Name"))
8252                    + " " + boundary.getConnect1().getLayoutBlock().getId());
8253            if (boundary.getConnect2() != null) {
8254                block2NameLabel.setText(Bundle.getMessage("MakeLabel",
8255                        Bundle.getMessage("BeanNameBlock") + " 2 "
8256                        + Bundle.getMessage("Name"))
8257                        + " " + boundary.getConnect2().getLayoutBlock().getId());
8258                block2NameLabel.setVisible(true);
8259            } else {
8260                block2NameLabel.setVisible(false);
8261            }
8262            getSavedAnchorSignalMasts(null);
8263        }
8264
8265        if (!setSignalMastsAtBlockBoundaryOpenFlag) {
8266            setSignalMastsAtBlockBoundaryFrame.setPreferredSize(null);
8267            setSignalMastsAtBlockBoundaryFrame.pack();
8268            setSignalMastsAtBlockBoundaryOpenFlag = true;
8269        }
8270        refreshSignalMastAtBoundaryComboBox();
8271        setSignalMastsAtBlockBoundaryFrame.setVisible(true);
8272    }
8273
8274    /**
8275     * Returns the SignalMast corresponding to an entry field in the specified
8276     * dialog. This also takes care of UpperCase and trimming of leading and
8277     * trailing blanks. If entry is required, and no entry is present, and error
8278     * message is sent. An error message also results if a signalMast head with
8279     * the entered name is not found in the SignalMastTable.
8280     * @param signalMastName name of the signal mast.
8281     * @param requireEntry true if a required field, else false.
8282     * @param frame main frame.
8283     * @return signal mast, may be null.
8284     */
8285    @CheckReturnValue
8286    public SignalMast getSignalMastFromEntry(@CheckForNull String signalMastName,
8287            boolean requireEntry,
8288            @Nonnull JmriJFrame frame) {
8289        if ((signalMastName == null) || signalMastName.isEmpty()) {
8290            if (requireEntry) {
8291                JmriJOptionPane.showMessageDialog(frame, Bundle.getMessage("SignalMastsError5"),
8292                        Bundle.getMessage("ErrorTitle"),
8293                        JmriJOptionPane.ERROR_MESSAGE);
8294            }
8295            return null;
8296
8297        }
8298        SignalMast head = InstanceManager.getDefault(SignalMastManager.class
8299        ).getSignalMast(signalMastName);
8300        if (head == null) {
8301            JmriJOptionPane.showMessageDialog(frame,
8302                    Bundle.getMessage("SignalMastsError4",
8303                            new Object[]{signalMastName}), Bundle.getMessage("ErrorTitle"),
8304                    JmriJOptionPane.ERROR_MESSAGE);
8305            return null;
8306        }
8307        return (head);
8308    }
8309
8310    /**
8311     * Returns true if the specified SignalMast is assigned to an object on the
8312     * panel, regardless of whether an icon is displayed or not.
8313     * @param signalMast the signal mast to query.
8314     * @return true if associated with panel, else false.
8315     */
8316    public boolean isSignalMastAssignedAnywhere(@Nonnull SignalMast signalMast) {
8317        boolean result = false;
8318        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
8319            if ((po.getEastBoundSignalMast() != null) && po.getEastBoundSignalMast() == signalMast) {
8320                result = true;
8321                break;
8322            }
8323            if ((po.getWestBoundSignalMast() != null) && po.getWestBoundSignalMast() == signalMast) {
8324                result = true;
8325                break;
8326            }
8327        }
8328
8329        if (!result) {
8330            for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
8331                if ((to.getSignalAMast() != null) && to.getSignalAMast() == signalMast) {
8332                    result = true;
8333                    break;
8334                }
8335                if ((to.getSignalBMast() != null) && to.getSignalBMast() == signalMast) {
8336                    result = true;
8337                    break;
8338                }
8339                if ((to.getSignalCMast() != null) && to.getSignalCMast() == signalMast) {
8340                    result = true;
8341                    break;
8342                }
8343                if ((to.getSignalDMast() != null) && to.getSignalDMast() == signalMast) {
8344                    result = true;
8345                    break;
8346                }
8347            }
8348        }
8349
8350        if (!result) {
8351            for (LevelXing x : layoutEditor.getLevelXings()) {
8352                if ((x.getSignalAMast() != null) && x.getSignalAMast() == signalMast) {
8353                    result = true;
8354                    break;
8355                }
8356                if ((x.getSignalBMast() != null) && x.getSignalBMast() == signalMast) {
8357                    result = true;
8358                    break;
8359                }
8360                if ((x.getSignalCMast() != null) && x.getSignalCMast() == signalMast) {
8361                    result = true;
8362                    break;
8363                }
8364                if ((x.getSignalDMast() != null) && x.getSignalDMast() == signalMast) {
8365                    result = true;
8366                    break;
8367                }
8368            }
8369        }
8370        return result;
8371    }
8372
8373    /**
8374     * Removes the assignment of the specified SignalMast to either a turnout, a
8375     * positionable point, or a level crossing wherever it is assigned.
8376     * @param signalMast the signal mast to remove.
8377     */
8378    public void removeSignalMastAssignment(@CheckForNull SignalMast signalMast) {
8379        if (signalMast == null) {
8380            return;
8381        }
8382
8383        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
8384            if ((po.getEastBoundSignalMast() != null) && po.getEastBoundSignalMast() == signalMast) {
8385                po.setEastBoundSignalMast(null);
8386            }
8387            if ((po.getWestBoundSignalMast() != null) && po.getWestBoundSignalMast() == signalMast) {
8388                po.setWestBoundSignalMast(null);
8389            }
8390        }
8391        for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
8392            if ((to.getSignalAMast() != null) && to.getSignalAMast() == signalMast) {
8393                to.setSignalAMast(null);
8394            }
8395            if ((to.getSignalBMast() != null) && to.getSignalBMast() == signalMast) {
8396                to.setSignalBMast(null);
8397            }
8398            if ((to.getSignalCMast() != null) && to.getSignalCMast() == signalMast) {
8399                to.setSignalCMast(null);
8400            }
8401            if ((to.getSignalDMast() != null) && to.getSignalDMast() == signalMast) {
8402                to.setSignalDMast(null);
8403            }
8404        }
8405
8406        for (LevelXing x : layoutEditor.getLevelXings()) {
8407            if ((x.getSignalAMast() != null) && x.getSignalAMast() == signalMast) {
8408                x.setSignalAMast(null);
8409            }
8410
8411            if ((x.getSignalBMast() != null) && x.getSignalBMast() == signalMast) {
8412                x.setSignalBMast(null);
8413            }
8414
8415            if ((x.getSignalCMast() != null) && x.getSignalCMast() == signalMast) {
8416                x.setSignalCMast(null);
8417            }
8418
8419            if ((x.getSignalDMast() != null) && x.getSignalDMast() == signalMast) {
8420                x.setSignalDMast(null);
8421            }
8422        }
8423    }
8424
8425    /**
8426     * Removes the SignalMast with the specified name from the panel and from
8427     * assignment to any turnout, positionable point, or level crossing.
8428     * @param signalMast the signal mast to remove.
8429     */
8430    public void removeSignalMastFromPanel(@Nonnull SignalMast signalMast) {
8431        removeSignalMastAssignment(signalMast);
8432        SignalMastIcon h = null;
8433        int index = -1;
8434        for (int i = 0; (i < layoutEditor.getSignalMastList().size()) && (index == -1); i++) {
8435            h = layoutEditor.getSignalMastList().get(i);
8436            if ((h != null) && (h.getSignalMast() == signalMast)) {
8437                index = i;
8438            }
8439        }
8440        if ((h != null) && (index != -1)) {
8441            layoutEditor.getSignalMastList().remove(index);
8442            h.remove();
8443            h.dispose();
8444            needRedraw = true;
8445        }
8446    }
8447
8448    private void getSavedAnchorSignalMasts(ActionEvent a) {
8449        if (!getSimpleBlockInformation()) {
8450            return;
8451        }
8452
8453        eastSignalMast.setTextField(boundary.getEastBoundSignalMastName());
8454        westSignalMast.setTextField(boundary.getWestBoundSignalMastName());
8455
8456        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
8457            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8458                eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8459            } else {
8460                eastSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8461            }
8462            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8463                westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8464            } else {
8465                westSignalMast.setBoundaryLabelText(Bundle.getMessage("ProtectingBlock") + boundary.getConnect2().getLayoutBlock().getDisplayName());
8466            }
8467        } else {
8468            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8469                westSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8470            } else {
8471                eastSignalMast.setBoundaryLabelText(Bundle.getMessage("EndOfBlock") + boundary.getConnect1().getLayoutBlock().getDisplayName());
8472            }
8473        }
8474        setSignalMastsAtBlockBoundaryFrame.setPreferredSize(null);
8475        setSignalMastsAtBlockBoundaryFrame.pack();
8476    }
8477
8478    private void setSignalMastsAtBlockBoundaryCancelPressed(ActionEvent a) {
8479        setSignalMastsAtBlockBoundaryOpenFlag = false;
8480        setSignalMastsAtBlockBoundaryFrame.setVisible(false);
8481    }
8482
8483    void refreshSignalMastAtBoundaryComboBox() {
8484        createListUsedSignalMasts();
8485        usedMasts.remove(eastSignalMast.getBean());
8486        usedMasts.remove(westSignalMast.getBean());
8487        eastSignalMast.getCombo().setExcludedItems(usedMasts);
8488        westSignalMast.getCombo().setExcludedItems(usedMasts);
8489    }
8490
8491    private void setSignalMastsAtBlockBoundaryDonePressed(ActionEvent a) {
8492        if (!getSimpleBlockInformation()) {
8493            return;
8494        }
8495
8496        SignalMast oldBlock1SignalMast = boundary.getEastBoundSignalMast();
8497        SignalMast block1BoundSignalMast = getSignalMastFromEntry(eastSignalMast.getText(), false, setSignalMastsAtBlockBoundaryFrame);
8498
8499        if (block1BoundSignalMast == null) {
8500            if (InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()
8501                    && InstanceManager.getDefault(SignalMastLogicManager.class).isSignalMastUsed(oldBlock1SignalMast)) {
8502                SignallingGuiTools.removeSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock1SignalMast);
8503            }
8504
8505            removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8506            removeSignalMastAssignment(boundary.getEastBoundSignalMast());
8507            boundary.setEastBoundSignalMast("");
8508        }
8509
8510        SignalMast oldBlock2SignalMast = boundary.getWestBoundSignalMast();
8511        SignalMast block2BoundSignalMast = getSignalMastFromEntry(westSignalMast.getText(), false, setSignalMastsAtBlockBoundaryFrame);
8512
8513        if (block2BoundSignalMast == null) {
8514            if (InstanceManager.getDefault(LayoutBlockManager.class
8515            ).isAdvancedRoutingEnabled() && InstanceManager.getDefault(SignalMastLogicManager.class
8516            ).isSignalMastUsed(oldBlock2SignalMast)) {
8517                SignallingGuiTools.removeSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock2SignalMast);
8518            }
8519
8520            removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8521            removeSignalMastAssignment(boundary.getWestBoundSignalMast());
8522            boundary.setWestBoundSignalMast("");
8523        }
8524        if (block2BoundSignalMast != null && block1BoundSignalMast != null) {
8525            if (block1BoundSignalMast == block2BoundSignalMast) {
8526                JmriJOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8527                        Bundle.getMessage("SignalMastsError14"),
8528                        Bundle.getMessage("ErrorTitle"),
8529                        JmriJOptionPane.ERROR_MESSAGE);
8530                return;
8531            }
8532            if (oldBlock1SignalMast == block2BoundSignalMast && oldBlock2SignalMast == block1BoundSignalMast) {
8533                //We are going for a swap!
8534                //Need to remove old items first
8535                removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8536                removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8537                removeSignalMastAssignment(block1BoundSignalMast);
8538                removeSignalMastAssignment(block2BoundSignalMast);
8539                //Then place new ones
8540                SignalMastIcon l;
8541                if (eastSignalMast.addToPanel()) {
8542                    l = new SignalMastIcon(layoutEditor);
8543                    l.setSignalMast(eastSignalMast.getText());
8544                    placeEastBoundIcon(l, eastSignalMast.isRightSelected(), 0);
8545                }
8546                if (westSignalMast.addToPanel()) {
8547                    l = new SignalMastIcon(layoutEditor);
8548                    l.setSignalMast(westSignalMast.getText());
8549                    placeWestBoundIcon(l, westSignalMast.isRightSelected(), 0);
8550                }
8551                boundary.setEastBoundSignalMast(eastSignalMast.getText());
8552                boundary.setWestBoundSignalMast(westSignalMast.getText());
8553                //Then sort out the logic
8554
8555                if (InstanceManager.getDefault(LayoutBlockManager.class
8556                ).isAdvancedRoutingEnabled()) {
8557                    SignallingGuiTools.swapSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, block1BoundSignalMast, block2BoundSignalMast);
8558                }
8559                needRedraw = true;
8560            }
8561        }
8562        if (!needRedraw) {
8563            if (block1BoundSignalMast != null) {
8564                if (eastSignalMast.addToPanel()) {
8565                    if (isSignalMastAssignedAnywhere(block1BoundSignalMast)
8566                            && (block1BoundSignalMast != oldBlock1SignalMast)) {
8567                        JmriJOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8568                                Bundle.getMessage("SignalMastsError6",
8569                                        new Object[]{eastSignalMast.getText()}),
8570                                Bundle.getMessage("ErrorTitle"),
8571                                JmriJOptionPane.ERROR_MESSAGE);
8572                        return;
8573                    } else {
8574                        removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8575                        SignalMastIcon l = new SignalMastIcon(layoutEditor);
8576                        l.setSignalMast(eastSignalMast.getText());
8577                        placeEastBoundIcon(l, eastSignalMast.isRightSelected(), 0);
8578                        removeSignalMastAssignment(block1BoundSignalMast);
8579                        boundary.setEastBoundSignalMast(eastSignalMast.getText());
8580                        needRedraw = true;
8581                    }
8582                } else if ((block1BoundSignalMast != boundary.getEastBoundSignalMast())
8583                        && (block1BoundSignalMast != boundary.getWestBoundSignalMast())) {
8584                    if (isSignalMastOnPanel(block1BoundSignalMast)) {
8585                        JmriJOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8586                                Bundle.getMessage("SignalMastsError13",
8587                                        new Object[]{eastSignalMast.getText()}),
8588                                Bundle.getMessage("ErrorTitle"),
8589                                JmriJOptionPane.ERROR_MESSAGE);
8590                        return;
8591                    } else {
8592                        removeSignalMastFromPanel(boundary.getEastBoundSignalMast());
8593                        removeSignalMastAssignment(block1BoundSignalMast);
8594                        boundary.setEastBoundSignalMast(eastSignalMast.getText());
8595                    }
8596                }
8597            }
8598            if (block2BoundSignalMast != null) {
8599                if (westSignalMast.addToPanel()) {
8600                    if (isSignalMastAssignedAnywhere(block2BoundSignalMast)
8601                            && (block2BoundSignalMast != oldBlock2SignalMast)) {
8602                        JmriJOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8603                                Bundle.getMessage("SignalMastsError6",
8604                                        new Object[]{westSignalMast.getText()}),
8605                                Bundle.getMessage("ErrorTitle"),
8606                                JmriJOptionPane.ERROR_MESSAGE);
8607                        return;
8608                    } else /*(oldBlock2SignalMast!=block2BoundSignalMast)*/ {
8609                        removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8610                        SignalMastIcon l = new SignalMastIcon(layoutEditor);
8611                        l.setSignalMast(westSignalMast.getText());
8612                        placeWestBoundIcon(l, westSignalMast.isRightSelected(), 0);
8613                        removeSignalMastAssignment(block2BoundSignalMast);
8614                        boundary.setWestBoundSignalMast(westSignalMast.getText());
8615                        needRedraw = true;
8616                    }
8617                } else if ((block2BoundSignalMast != boundary.getEastBoundSignalMast())
8618                        && (block2BoundSignalMast != oldBlock2SignalMast)) {
8619                    if (isSignalMastAssignedAnywhere(block2BoundSignalMast)) {
8620                        //Need to do this better, so that the signalMast can be on panel multiple times but only alocated to one anchor at a time
8621                        JmriJOptionPane.showMessageDialog(setSignalMastsAtBlockBoundaryFrame,
8622                                Bundle.getMessage("SignalMastsError13",
8623                                        new Object[]{westSignalMast.getText()}),
8624                                Bundle.getMessage("ErrorTitle"),
8625                                JmriJOptionPane.ERROR_MESSAGE);
8626                        return;
8627                    } else {
8628                        removeSignalMastFromPanel(boundary.getWestBoundSignalMast());
8629                        removeSignalMastAssignment(block2BoundSignalMast);
8630                        boundary.setWestBoundSignalMast(westSignalMast.getText());
8631
8632                    }
8633                }
8634            }
8635
8636            //If advanced routing is enabled and then this indicates that we are using this for discovering the signalmast logic paths.
8637            if (InstanceManager.getDefault(LayoutBlockManager.class
8638            ).isAdvancedRoutingEnabled()
8639                    && (block1BoundSignalMast != null
8640                    || block2BoundSignalMast != null)) {
8641                if ((oldBlock1SignalMast != null) && (block2BoundSignalMast != null)) {
8642                    updateBoundaryBasedSignalMastLogic(
8643                            oldBlock1SignalMast, oldBlock2SignalMast,
8644                            block1BoundSignalMast, block2BoundSignalMast);
8645
8646                }
8647            }
8648        }
8649        setSignalMastsAtBlockBoundaryOpenFlag = false;
8650        setSignalMastsAtBlockBoundaryFrame.setVisible(false);
8651        if (needRedraw) {
8652            layoutEditor.redrawPanel();
8653            needRedraw = false;
8654            layoutEditor.setDirty();
8655        }
8656    }
8657
8658    public void updateBoundaryBasedSignalMastLogic(
8659            @Nonnull SignalMast oldBlock1SignalMast,
8660            @Nonnull SignalMast oldBlock2SignalMast,
8661            @Nonnull SignalMast block1BoundSignalMast,
8662            @Nonnull SignalMast block2BoundSignalMast) {
8663        SignalMastLogicManager smlm = InstanceManager.getDefault(SignalMastLogicManager.class
8664        );
8665        boolean old1Used = smlm.isSignalMastUsed(oldBlock1SignalMast);
8666        boolean old2Used = smlm.isSignalMastUsed(oldBlock2SignalMast);
8667        //Just check that the old ones are used in logics somewhere.
8668        if (old1Used || old2Used) {
8669            boolean new1Used = smlm.isSignalMastUsed(block1BoundSignalMast);
8670            boolean new2Used = smlm.isSignalMastUsed(block2BoundSignalMast);
8671            if (new1Used || new2Used) {
8672                if ((new1Used) && (block1BoundSignalMast != oldBlock1SignalMast)) {
8673                    SignallingGuiTools.removeAlreadyAssignedSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, block1BoundSignalMast);
8674                }
8675                if ((new2Used) && (block2BoundSignalMast != oldBlock2SignalMast)) {
8676                    SignallingGuiTools.removeAlreadyAssignedSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, block2BoundSignalMast);
8677                }
8678            }
8679            if (block1BoundSignalMast != null) {
8680                if (oldBlock2SignalMast != null && old2Used
8681                        && oldBlock2SignalMast == block1BoundSignalMast) {
8682                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock2SignalMast, block1BoundSignalMast);
8683                }
8684
8685                if (oldBlock1SignalMast != null && old1Used
8686                        && oldBlock1SignalMast != block1BoundSignalMast) {
8687
8688                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock1SignalMast, block1BoundSignalMast);
8689                }
8690            }
8691            if (block2BoundSignalMast != null) {
8692                if (old1Used && oldBlock1SignalMast == block2BoundSignalMast) {
8693
8694                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock1SignalMast, block2BoundSignalMast);
8695                }
8696                if (old2Used && oldBlock2SignalMast != block2BoundSignalMast) {
8697                    SignallingGuiTools.updateSignalMastLogic(setSignalMastsAtBlockBoundaryFrame, oldBlock2SignalMast, block2BoundSignalMast);
8698                }
8699            }
8700        }
8701    }
8702
8703    public void setIconOnPanel(@Nonnull PositionableIcon l,
8704            int rotation, @Nonnull Point p) {
8705        setIconOnPanel(l, rotation, (int) p.getX(), (int) p.getY());
8706    }
8707
8708    public void setIconOnPanel(@Nonnull PositionableIcon l,
8709            int rotation, int xLoc, int yLoc) {
8710        l.setLocation(xLoc, yLoc);
8711        if (rotation > 0) {
8712            l.rotate(rotation);
8713        }
8714        if (l instanceof SignalMastIcon) {
8715            layoutEditor.putSignalMast((SignalMastIcon) l);
8716        } else if (l instanceof SensorIcon) {
8717            layoutEditor.putSensor((SensorIcon) l);
8718        } else if (l instanceof SignalHeadIcon) {
8719            layoutEditor.putSignal((SignalHeadIcon) l);
8720        }
8721    }
8722
8723    private void placeEastBoundIcon(PositionableIcon icon, boolean isRightSide, double fromPoint) {
8724
8725        Point2D p = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
8726
8727        //Track segment is used to determine the alignment,
8728        // therefore this is opposite to the block that we are protecting
8729
8730        //For edge connectors we need to do two things:
8731        // - change the dir value as east and west are swapped
8732        // - and as we have only one track segment on any one layout editor
8733        // so we determine where the track segment would end if we did a mirror image
8734        // so that the calculations for angled segments is correct
8735
8736        TrackSegment t = boundary.getConnect2();
8737        boolean dir = true;
8738        boolean shouldUseConnect2 = true;
8739
8740        if (boundary.getType() == PositionablePoint.PointType.END_BUMPER) {
8741            t = boundary.getConnect1();
8742        } else{
8743            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8744                t = boundary.getConnect1();
8745            }
8746        }
8747
8748        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8749            t = boundary.getConnect1();  //have to use connect1 as there is only one track segment attached to edge connector
8750            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8751                shouldUseConnect2 = false;
8752            }
8753        }
8754
8755        Point2D pt2;
8756        if (t.getConnect1() == boundary){
8757            pt2 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
8758        } else {
8759            pt2 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
8760        }
8761
8762        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8763            Point2D p3 = (Point2D) pt2.clone();
8764            if (shouldUseConnect2) {
8765                //use a point opposite pt2  (= p + p- pt2 using vector addition)
8766                double px = p.getX();
8767                double py = p.getY();
8768                double p2x = pt2.getX();
8769                double p2y = pt2.getY();
8770                double p3x = px + px - p2x;
8771                double p3y = py + py - p2y;
8772                p3.setLocation(p3x, p3y);
8773            }
8774            dir = false;  //east and west are swapped with edge connectors
8775            setIconOnPanel(t, icon, dir, p, p3, isRightSide, fromPoint);
8776        } else{
8777            setIconOnPanel(t, icon, dir, p, pt2, isRightSide, fromPoint);
8778        }
8779
8780    }
8781
8782    private void placeWestBoundIcon(PositionableIcon icon, boolean isRightSide, double fromPoint) {
8783
8784        Point2D p = layoutEditor.getLayoutTrackView(boundary).getCoordsCenter();
8785
8786        //Track segment is used to determine the alignment,
8787        // therefore this is opposite to the block that we are protecting
8788
8789        //For edge connectors we need to do two things:
8790        // - change the dir value as east and west are swapped
8791        // - and as we have only one track segment on any one layout editor
8792        // so we determine where the track segment would end if we did a mirror image
8793        // so that the calculations for angled segments is correct
8794
8795        TrackSegment t = boundary.getConnect1();
8796        boolean dir = false;
8797        boolean shouldUseConnect2 = false;
8798        if (boundary.getType() != PositionablePoint.PointType.END_BUMPER) {
8799            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8800                t = boundary.getConnect2();
8801            }
8802        }
8803
8804        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8805            t = boundary.getConnect1(); //have to use connect1 as there is only one track segment attached to edge connector
8806            if (isAtWestEndOfAnchor(boundary.getConnect1(), boundary)) {
8807                shouldUseConnect2 = true;
8808            }
8809        }
8810
8811        Point2D pt2;
8812        if (t.getConnect1() == boundary) {
8813            pt2 = layoutEditor.getCoords(t.getConnect2(), t.getType2());
8814        } else {
8815            pt2 = layoutEditor.getCoords(t.getConnect1(), t.getType1());
8816        }
8817
8818        if (boundary.getType() == PositionablePoint.PointType.EDGE_CONNECTOR) {
8819            Point2D p3 = (Point2D) pt2.clone();
8820            if (shouldUseConnect2) {
8821                //use a point opposite pt2  (= p + p - pt2 using vector addition)
8822                double px = p.getX();
8823                double py = p.getY();
8824                double p2x = pt2.getX();
8825                double p2y = pt2.getY();
8826                double p3x = px + px - p2x;
8827                double p3y = py + py - p2y;
8828                p3.setLocation(p3x, p3y);
8829            }
8830            dir = true;  //east and west are swapped with edge connectors
8831            setIconOnPanel(t, icon, dir, p, p3, isRightSide, fromPoint);
8832        } else{
8833            setIconOnPanel(t, icon, dir, p, pt2, isRightSide, fromPoint);
8834        }
8835    }
8836
8837    private void setIconOnPanel(@Nonnull TrackSegment t,
8838            @Nonnull PositionableIcon l,
8839            boolean isEastBound, @Nonnull Point2D pt1, @Nonnull Point2D pt2,
8840            boolean isRightSide, double fromPoint) {
8841        double pt1x = pt1.getX(), pt1y = pt1.getY();
8842        double pt2x = pt2.getX(), pt2y = pt2.getY();
8843
8844        int triX = (int) Math.round(pt2x - pt1x);
8845        int triY = (int) Math.round(pt2y - pt1y);
8846
8847        if (log.isDebugEnabled()) {
8848            log.debug("X {} Y {}", triX, triY);
8849        }
8850        Point loc;
8851        if (triX == 0 || triX == 360) {
8852            //In a vertical Straight Line
8853            if (isEastBound) {
8854                log.debug("In a vertical straightline facing South");
8855                loc = northToSouth(pt1, l, isRightSide, fromPoint);
8856            } else {
8857                log.debug("In a vertical striaghtline facing North");
8858                loc = southToNorth(pt1, l, isRightSide, fromPoint);
8859            }
8860        } else if (triY == 0 || triY == 360) {
8861            //In a Horizontal Straight Line
8862            if (isEastBound) {
8863                log.debug("In a Horizontal striaghtline facing east");
8864                loc = westToEast(pt1, l, isRightSide, fromPoint);
8865            } else {
8866                log.debug("In a Horizontal striaghtline facing west");
8867                loc = eastToWest(pt1, l, isRightSide, fromPoint);
8868            }
8869        } else {
8870            //Compute arc's chord
8871            double a = pt2x - pt1x;
8872            double o = pt2y - pt1y;
8873            double radius = Math.hypot(a, o);  //chord equates to radius of circle
8874
8875            double pt1xa = pt1x + radius;
8876            double a1 = pt2x - pt1xa;
8877            double o1 = pt2y - pt1y;
8878            double chord = Math.hypot(a1, o1);
8879            double rsq = Math.pow(radius, 2);
8880
8881            double radAngleFromDatum = Math.acos((rsq + rsq - Math.pow(chord, 2)) / (2 * rsq));
8882            if (log.isDebugEnabled()) {
8883                log.debug("radius {} Chord {}", radius, chord);
8884                log.debug("Angle from datum line {}", Math.toDegrees(radAngleFromDatum));
8885            }
8886
8887            int rotateDEG = ((int) Math.toDegrees(radAngleFromDatum));
8888
8889            if (log.isDebugEnabled()) {
8890                double tanx = o / a;
8891                double angletanRAD = Math.atan2(o, a);
8892                log.debug("{} = atan2({}, {}) ({})", Math.toDegrees(angletanRAD), o, a, tanx);
8893            }
8894
8895            int oldHeight = l.maxHeight();
8896            int oldWidth = l.maxWidth();
8897
8898            //pt1 is always our boundary point
8899            //East side
8900            if (pt2x > pt1x) {
8901                //East Sides
8902                if (pt2y > pt1y) {
8903                    //"South East Corner"
8904                    rotateDEG = rotateDEG + 270;  //Correct for SM111, sm101, sm121, SM80
8905                    l.rotate(rotateDEG);
8906                    loc = southEastToNorthWest(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8907                } else {
8908                    //"North East corner" //correct for sm110, sm70, sm131
8909                    rotateDEG = 270 - rotateDEG;
8910                    l.rotate(rotateDEG);
8911                    loc = northEastToSouthWest(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8912                }
8913
8914            } else {
8915                //West Side
8916                if (pt2y > pt1y) {
8917                    //South West //WORKING FOR SM141, sm130, SM71
8918                    l.rotate(rotateDEG - 90);
8919                    //South West
8920                    loc = southWestToNorthEast(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8921                } else {
8922                    //North West //Working FOR SM140, SM81, sm120
8923                    rotateDEG = (180 - rotateDEG) + 90;
8924                    l.rotate(rotateDEG);
8925                    loc = northWestToSouthEast(pt1, l, oldWidth, oldHeight, rotateDEG, isRightSide, fromPoint);
8926                }
8927            }
8928        }
8929        setIconOnPanel(l, 0, loc);
8930    }
8931
8932    Point southToNorth(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8933        int offsetx = 0;
8934        int offsety = (int) (p.getY() + offSetFromPoint + fromPoint);
8935        if (right) {
8936            offsetx = (int) p.getX() + offSetFromPoint;
8937        } else {
8938            offsetx = (int) p.getX() - offSetFromPoint - l.maxWidth();
8939        }
8940        return new Point(offsetx, offsety);
8941    }
8942
8943    Point northToSouth(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8944        l.rotate(180);
8945        int offsetx = 0;
8946        int offsety = (int) (p.getY() - (offSetFromPoint + fromPoint) - l.maxHeight());
8947        if (right) {
8948            offsetx = (int) p.getX() - offSetFromPoint - l.maxWidth();
8949        } else {
8950            offsetx = (int) p.getX() + offSetFromPoint;
8951        }
8952        return new Point(offsetx, offsety);
8953    }
8954
8955    Point westToEast(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8956        l.rotate(90);
8957        int offsetx = (int) (p.getX() - (l.maxWidth() + (offSetFromPoint + fromPoint - 1)));
8958        int offsety = 0;
8959        if (right) {
8960            offsety = (int) p.getY() + (offSetFromPoint - 1);
8961        } else {
8962            offsety = (int) p.getY() - (offSetFromPoint) - l.maxHeight();
8963        }
8964        return new Point(offsetx, offsety);
8965    }
8966
8967    Point eastToWest(Point2D p, PositionableIcon l, boolean right, double fromPoint) {
8968        l.rotate(-90);
8969        int offsetx = (int) (p.getX() + offSetFromPoint + fromPoint);
8970        int offsety = 0;
8971        if (right) {
8972            offsety = (int) p.getY() - (offSetFromPoint - 1) - l.maxHeight();
8973        } else {
8974            offsety = (int) p.getY() + (offSetFromPoint);
8975        }
8976        return new Point(offsetx, offsety);
8977    }
8978
8979     // Come back to this as its a bit tight to the rail on SM110 need re
8980     // checking
8981    Point northEastToSouthWest(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
8982        angleDEG = angleDEG - 180;
8983        if (angleDEG < 45) {
8984            //Because of the angle things get shifted about.
8985            int tmpWidth = oldWidth;
8986            oldWidth = oldHeight;
8987            oldHeight = tmpWidth;
8988        }
8989        double ang = angleDEG;
8990        double oppAng = 90 - ang;
8991        double angleRAD = Math.toRadians(angleDEG);
8992        double oppAngRAD = Math.toRadians(oppAng);
8993        double iconAdj = Math.sin(angleRAD) * oldHeight;
8994        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
8995        double bpa = Math.sin(angleRAD) * (offSetFromPoint + fromPoint);
8996        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
8997        double ta = Math.sin(angleRAD) * offSetFromPoint;
8998        double to = Math.sin(oppAngRAD) * offSetFromPoint;
8999
9000        if (log.isDebugEnabled()) {
9001            log.debug("north east to south west {}", angleDEG);
9002            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9003            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9004            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
9005            log.debug("boundary point opp {}", bpo);
9006            log.debug("boundary point adj {}", bpa);
9007            log.debug("track opp {}", to);
9008            log.debug("track adj {}", ta);
9009        }
9010        int xpos = 0;
9011        int ypos = 0;
9012        if (right) {
9013            //double x_dist_to_Icon = (l.maxWidth()-iconAdj)-(bpa-bpo);
9014            //double y_dist_to_Icon = bpa+bpo+l.maxHeight();
9015
9016            double x_dist_to_Icon = (iconAdjOpp) - (bpa - to);
9017            double y_dist_to_Icon = ta + bpo + l.maxHeight();
9018
9019            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9020
9021            xpos = (int) (p.getX() - x_dist_to_Icon);
9022            ypos = (int) (p.getY() - y_dist_to_Icon);
9023
9024        } else {
9025            double y_dist_to_Icon = iconAdjOpp + (bpo - ta);
9026            double x_dist_to_Icon = to + bpa;
9027            //double y_dist_to_Icon = (l.maxHeight()-iconAdj)-(ta-bpo);
9028            //double x_dist_to_Icon = bpa+to;
9029            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9030
9031            xpos = (int) (p.getX() + x_dist_to_Icon);
9032            ypos = (int) (p.getY() - y_dist_to_Icon);
9033
9034        }
9035        if (log.isDebugEnabled()) {
9036            log.debug("xpos {}", xpos);
9037            log.debug("yPos {}", ypos);
9038        }
9039        return new Point(xpos, ypos);
9040
9041    }
9042
9043    Point southWestToNorthEast(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
9044        angleDEG = 180 - angleDEG;
9045
9046        double oppAng = angleDEG;
9047        double angDEG = 90 - oppAng;
9048
9049        //Because of the angle things get shifted about.
9050        if (angDEG < 45) { //was angle
9051            int tmpWidth = oldWidth;
9052            oldWidth = oldHeight;
9053            oldHeight = tmpWidth;
9054        }
9055
9056        double angRAD = Math.toRadians(angDEG);
9057        double oppAngRAD = Math.toRadians(oppAng);
9058        double iconAdj = Math.sin(angRAD) * oldHeight;
9059        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
9060        double bpa = Math.sin(angRAD) * (offSetFromPoint + fromPoint);
9061        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
9062        double ta = Math.sin(angRAD) * offSetFromPoint;
9063        double to = Math.sin(oppAngRAD) * offSetFromPoint;
9064
9065        if (log.isDebugEnabled()) {
9066            log.debug("south west to north east {}", angleDEG);
9067            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9068            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9069            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
9070            log.debug("boundary point opp {}", bpo);
9071            log.debug("boundary point adj {}", bpa);
9072            log.debug("track opp {}", to);
9073            log.debug("track adj {}", ta);
9074        }
9075
9076        int xpos;
9077        int ypos;
9078
9079        if (right) {
9080            double x_dist_to_Icon = iconAdj + (bpa - to);
9081            double y_dist_to_Icon = ta + bpo;
9082            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9083
9084            xpos = (int) (p.getX() - x_dist_to_Icon);
9085            log.debug("xpos {}", xpos);
9086            ypos = (int) (p.getY() + y_dist_to_Icon);
9087            log.debug("yPos {}", ypos);
9088        } else {
9089            double x_dist_to_Icon = (bpa + to) + l.maxWidth();
9090            //double y_dist_to_Icon = (iconAdj+(ta-bpo));
9091            double y_dist_to_Icon = (bpo - ta) - (l.maxHeight() - iconAdjOpp);
9092            //double y_dist_to_Icon = (iconAdj+(ta-bpo));
9093            log.debug("x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9094            xpos = (int) (p.getX() - x_dist_to_Icon);
9095            ypos = (int) (p.getY() + y_dist_to_Icon);
9096        }
9097        if (log.isDebugEnabled()) {
9098            log.debug("xpos {}", xpos);
9099            log.debug("yPos {}", ypos);
9100        }
9101        return new Point(xpos, ypos);
9102
9103    }
9104
9105    //Working FOR SM140, SM81, sm120
9106    Point northWestToSouthEast(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
9107        log.debug("angle before {}", angleDEG);
9108        angleDEG = 180 - angleDEG;
9109        angleDEG = 90 - angleDEG;
9110        log.debug("north west to south east {}", angleDEG);
9111        if (angleDEG < 45) {
9112            //Because of the angle things get shifted about.
9113            int tmpWidth = oldWidth;
9114            oldWidth = oldHeight;
9115            oldHeight = tmpWidth;
9116        }
9117        log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9118        log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9119        //double ang = angle;
9120        double oppAng = 90 - angleDEG;
9121        double angleRAD = Math.toRadians(angleDEG);
9122        double oppAngRAD = Math.toRadians(oppAng);
9123        double iconAdj = Math.sin(angleRAD) * oldHeight;
9124        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
9125
9126        double bpa = Math.sin(angleRAD) * (offSetFromPoint + fromPoint);  //distance from point
9127        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
9128        double ta = Math.sin(angleRAD) * offSetFromPoint; //distance from track
9129        double to = Math.sin(oppAngRAD) * offSetFromPoint;
9130
9131        if (log.isDebugEnabled()) {
9132            log.debug("north west to south east {}", angleDEG);
9133            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9134            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9135            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
9136            log.debug("boundary point opp {}", bpo);
9137            log.debug("boundary point adj {}", bpa);
9138            log.debug("track opp {}", to);
9139            log.debug("track adj {}", ta);
9140        }
9141        int xpos = 0;
9142        int ypos = 0;
9143        if (right) {
9144            //double x_dist_to_Icon = bpa+bpo+l.maxWidth();
9145            //double y_dist_to_Icon = bpa-(l.maxHeight()-iconAdj);
9146            double x_dist_to_Icon = (l.maxWidth() + ta + bpo);
9147            double y_dist_to_Icon = iconAdj + (bpa - to);
9148
9149            log.debug("right x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9150
9151            xpos = (int) (p.getX() - x_dist_to_Icon);
9152            ypos = (int) (p.getY() - y_dist_to_Icon); //was +
9153        } else {
9154            //This still needs to be worked out.
9155            //double y_dist_to_Icon = bpa+bpo+l.maxHeight();
9156            //double x_dist_to_Icon = iconAdj+(bpa-bpo);
9157
9158            double y_dist_to_Icon = l.maxHeight() + bpa + to;//+(l.maxWidth()-iconAdj);
9159            //double y_dist_to_Icon = bpa-(l.maxHeight()-iconAdj);
9160            //double y_dist_to_Icon = ta+bpo+l.maxHeight();
9161            double x_dist_to_Icon = (iconAdjOpp) + (bpo - ta);
9162            //double x_dist_to_Icon = iconAdj+(bpa-to);
9163            log.debug("left x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9164
9165            xpos = (int) (p.getX() - x_dist_to_Icon);
9166            ypos = (int) (p.getY() - y_dist_to_Icon);
9167        }
9168        if (log.isDebugEnabled()) {
9169            log.debug("{} xpos {}", p.getX(), xpos);
9170            log.debug("{} yPos {}", p.getY(), ypos);
9171        }
9172        return new Point(xpos, ypos);
9173    }
9174
9175    double adjust = (5.0 / 90.0);
9176    int awayright = 5;
9177    private final int offSetFromPoint = 5;
9178
9179    //Correct for SM111, sm101, sm121, SM80
9180    Point southEastToNorthWest(Point2D p, PositionableIcon l, int oldWidth, int oldHeight, double angleDEG, boolean right, double fromPoint) {
9181        angleDEG = 360 - angleDEG;
9182
9183        if (angleDEG > 45) {
9184            //Because of the angle things get shifted about.
9185            int tmpWidth = oldWidth;
9186            int tmpHeight = oldHeight;
9187            oldWidth = tmpWidth;
9188            oldHeight = tmpHeight;
9189        }
9190
9191        // double ang = angle;
9192        double oppAng = 90 - angleDEG;
9193        double angleRAD = Math.toRadians(angleDEG);
9194        double oppAngRAD = Math.toRadians(oppAng);
9195        double iconAdj = Math.sin(angleRAD) * oldHeight;
9196        double iconAdjOpp = Math.sin(oppAngRAD) * oldHeight;
9197        double bpa = Math.sin(angleRAD) * (offSetFromPoint + fromPoint);
9198        double bpo = Math.sin(oppAngRAD) * (offSetFromPoint + fromPoint);
9199        double ta = Math.sin(angleRAD) * offSetFromPoint; //distance from track
9200        double to = Math.sin(oppAngRAD) * offSetFromPoint;
9201        if (log.isDebugEnabled()) {
9202            log.debug("south east to north west {}", angleDEG);
9203            log.debug("oldWidth {} oldHeight {}", oldWidth, oldHeight);
9204            log.debug("newWidth {} newHeight {}", l.maxWidth(), l.maxHeight());
9205            log.debug("Icon adj: {} opp adj: {}", iconAdj, iconAdjOpp);
9206            log.debug("boundary point opp {}", bpo);
9207            log.debug("boundary point adj {}", bpa);
9208            log.debug("track opp {}", to);
9209            log.debug("track adj {}", ta);
9210        }
9211        int xpos = 0;
9212        int ypos = 0;
9213        if (right) {
9214            //double x_dist_to_Icon = bpa+bpo;
9215            //double y_dist_to_Icon = (iconAdj+bpa-bpo);
9216            double x_dist_to_Icon = bpa + to;
9217            double y_dist_to_Icon = (bpo - ta) - (l.maxHeight() - iconAdjOpp);
9218
9219            log.debug("ydist {}", Double.toString((bpo - ta) - (l.maxHeight() - iconAdjOpp)));
9220            log.debug("   and {}",Double.toString(bpo - (iconAdj + ta)));
9221            /*if(angleDeg<45){
9222    y_dist_to_Icon = (bpo-ta)-(l.maxHeight()-iconAdjOpp);
9223    } else {
9224    y_dist_to_Icon = bpo-(iconAdj+ta);
9225    }*/
9226            //double y_dist_to_Icon = (l.maxHeight()-iconAdj)+(bpo-ta);
9227            xpos = (int) (p.getX() + x_dist_to_Icon);
9228            ypos = (int) (p.getY() + y_dist_to_Icon);
9229            log.debug("right x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9230        } else {
9231            //double x_dist_to_Icon = l.maxWidth()-(iconAdj+(bpa-bpo));
9232            //double y_dist_to_Icon = bpa+bpo;
9233
9234            double x_dist_to_Icon = (bpa - to) - (l.maxWidth() - iconAdj);
9235            double y_dist_to_Icon = bpo + ta;
9236
9237            xpos = (int) (p.getX() + x_dist_to_Icon);
9238            ypos = (int) (p.getY() + y_dist_to_Icon);
9239            log.debug("left x dist {}, y dist {}", x_dist_to_Icon, y_dist_to_Icon);
9240        }
9241        if (log.isDebugEnabled()) {
9242            log.debug("{} xpos {}", p.getX(), xpos);
9243            log.debug("{} yPos {}", p.getY(), ypos);
9244        }
9245
9246        return new Point(xpos, ypos);
9247    }
9248
9249    public boolean isSignalMastOnPanel(@Nonnull SignalMast signalMast) {
9250        for (SignalMastIcon s : layoutEditor.getSignalMastList()) {
9251            if (s.getSignalMast() == signalMast) {
9252                return true;
9253            }
9254        }
9255        return false;
9256    }
9257
9258    /*=========================*\
9259    |* setSignalMastsAtTurnout *|
9260    \*=========================*/
9261    private JmriJFrame setSignalMastsAtTurnoutFrame = null;
9262    private boolean setSignalMastsAtTurnoutOpenFlag = false;
9263    private boolean setSignalMastsAtTurnoutFromMenuFlag = false;
9264
9265    private final NamedBeanComboBox<Turnout> signalMastsTurnoutComboBox = new NamedBeanComboBox<>(
9266            InstanceManager.turnoutManagerInstance(), null,
9267            DisplayOptions.DISPLAYNAME);
9268
9269    private JButton setSignalMastsDone;
9270    private JButton getSavedSignalMasts;
9271    private JButton setSignalMastsCancel;
9272    private JLabel turnoutMastNameLabel = null;
9273
9274    BeanDetails<SignalMast> turnoutSignalMastA;
9275    BeanDetails<SignalMast> turnoutSignalMastB;
9276    BeanDetails<SignalMast> turnoutSignalMastC;
9277    BeanDetails<SignalMast> turnoutSignalMastD;
9278
9279    JPanel signalMastTurnoutPanel = new JPanel(new FlowLayout());
9280
9281    private String[] turnoutBlocks = new String[4];
9282
9283    public void setSignalMastsAtTurnoutFromMenu(@Nonnull LayoutTurnout to,
9284            @Nonnull String[] blocks) {
9285        layoutTurnout = to;
9286        turnout = to.getTurnout();
9287        signalMastsTurnoutComboBox.setSelectedItem(turnout);
9288        turnoutBlocks = new String[4];
9289        for (int i = 0; i < blocks.length; i++) {
9290            turnoutBlocks[i] = blocks[i];
9291        }
9292        setSignalMastsAtTurnoutFromMenuFlag = true;
9293        setSignalMastsAtTurnout();
9294        setSignalMastsAtTurnoutFromMenuFlag = false;
9295    }
9296
9297    //TODO: Add to Tools menu?
9298    public void setSignalMastsAtTurnout() {
9299
9300        //Initialize if needed
9301        if (setSignalMastsAtTurnoutFrame == null) {
9302            setSignalMastsAtTurnoutOpenFlag = false;
9303
9304            turnoutSignalMastA = new BeanDetails<>("SignalMast", // NOI18N
9305                    InstanceManager.getDefault(SignalMastManager.class));
9306            turnoutSignalMastB = new BeanDetails<>("SignalMast", // NOI18N
9307                    InstanceManager.getDefault(SignalMastManager.class));
9308            turnoutSignalMastC = new BeanDetails<>("SignalMast", // NOI18N
9309                    InstanceManager.getDefault(SignalMastManager.class));
9310            turnoutSignalMastD = new BeanDetails<>("SignalMast", // NOI18N
9311                    InstanceManager.getDefault(SignalMastManager.class));
9312
9313            turnoutSignalMastA.getDetailsPanel().setBackground(new Color(255, 255, 200));
9314            turnoutSignalMastB.getDetailsPanel().setBackground(new Color(200, 255, 255));
9315            turnoutSignalMastC.getDetailsPanel().setBackground(new Color(200, 200, 255));
9316            turnoutSignalMastD.getDetailsPanel().setBackground(new Color(255, 200, 200));
9317
9318            setSignalMastsAtTurnoutFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtTurnout"), false, true);
9319            oneFrameToRuleThemAll(setSignalMastsAtTurnoutFrame);
9320            setSignalMastsAtTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
9321//         setSignalMastsAtTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalMastsAtTurnout", true);
9322            setSignalMastsAtTurnoutFrame.setLocation(70, 30);
9323            Container theContentPane = setSignalMastsAtTurnoutFrame.getContentPane();
9324            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
9325
9326            JPanel panel1 = new JPanel(new FlowLayout());
9327
9328            turnoutMastNameLabel = new JLabel(
9329                    Bundle.getMessage("BeanNameTurnout")
9330                    + " " + Bundle.getMessage("Name"));
9331            panel1.add(turnoutMastNameLabel);
9332            panel1.add(signalMastsTurnoutComboBox);
9333            signalMastsTurnoutComboBox.setToolTipText(Bundle.getMessage("SignalMastsTurnoutNameHint"));
9334
9335            theContentPane.add(panel1);
9336            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
9337
9338            JPanel panel2 = new JPanel(new FlowLayout());
9339            JLabel shTitle = new JLabel(Bundle.getMessage("SignalMasts"));
9340            panel2.add(shTitle);
9341            panel2.add(new JLabel("   "));
9342            panel2.add(getSavedSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
9343            getSavedSignalMasts.addActionListener(this::turnoutSignalMastsGetSaved);
9344            getSavedSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
9345            theContentPane.add(panel2);
9346
9347            signalMastTurnoutPanel.setLayout(new GridLayout(0, 2));
9348            theContentPane.add(signalMastTurnoutPanel);
9349            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
9350
9351            JPanel panel6 = new JPanel(new FlowLayout());
9352            panel6.add(new JLabel("   "));
9353            panel6.add(setSignalMastsDone = new JButton(Bundle.getMessage("ButtonDone")));
9354            setSignalMastsDone.addActionListener(this::setSignalMastsDonePressed);
9355            setSignalMastsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
9356
9357            panel6.add(setSignalMastsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
9358            setSignalMastsCancel.addActionListener(this::setSignalMastsCancelPressed);
9359            setSignalMastsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
9360            theContentPane.add(panel6);
9361
9362//make this button the default button (return or enter activates)
9363            JRootPane rootPane = SwingUtilities.getRootPane(setSignalMastsDone);
9364            if (rootPane != null) {
9365                rootPane.setDefaultButton(setSignalMastsDone);
9366            }
9367        }
9368
9369        turnoutSignalMastA.getCombo().setExcludedItems(new HashSet<>());
9370        turnoutSignalMastB.getCombo().setExcludedItems(new HashSet<>());
9371        turnoutSignalMastC.getCombo().setExcludedItems(new HashSet<>());
9372        turnoutSignalMastD.getCombo().setExcludedItems(new HashSet<>());
9373        signalMastTurnoutPanel.removeAll();
9374
9375        signalMastsTurnoutComboBox.setVisible(!setSignalMastsAtTurnoutFromMenuFlag);
9376
9377        if (setSignalMastsAtTurnoutFromMenuFlag) {
9378            turnoutMastNameLabel.setText(Bundle.getMessage("MakeLabel",
9379                    Bundle.getMessage("BeanNameTurnout")
9380                    + " " + Bundle.getMessage("Name"))
9381                    + " " + layoutTurnout.getTurnoutName());
9382        }
9383
9384        if (!setSignalMastsAtTurnoutOpenFlag) {
9385            setSignalMastsAtTurnoutFrame.setPreferredSize(null);
9386            setSignalMastsAtTurnoutFrame.pack();
9387            setSignalMastsAtTurnoutOpenFlag = true;
9388        }
9389        refreshSignalMastAtTurnoutComboBox();
9390        setSignalMastsAtTurnoutFrame.setVisible(true);
9391    }   //setSignalMastsAtTurnout
9392
9393    private void turnoutSignalMastsGetSaved(ActionEvent a) {
9394        if (!getTurnoutMastInformation()) {
9395            return;
9396        }
9397        turnoutBlocks = layoutTurnout.getBlockBoundaries();
9398
9399        turnoutSignalMastA.setTextField(layoutTurnout.getSignalAMastName());
9400        turnoutSignalMastB.setTextField(layoutTurnout.getSignalBMastName());
9401        turnoutSignalMastC.setTextField(layoutTurnout.getSignalCMastName());
9402        turnoutSignalMastD.setTextField(layoutTurnout.getSignalDMastName());
9403
9404        turnoutSignalMastA.setBoundaryLabel(turnoutBlocks[0]);
9405        turnoutSignalMastB.setBoundaryLabel(turnoutBlocks[1]);
9406        turnoutSignalMastC.setBoundaryLabel(turnoutBlocks[2]);
9407        turnoutSignalMastD.setBoundaryLabel(turnoutBlocks[3]);
9408
9409        signalMastTurnoutPanel.removeAll();
9410        boolean boundaryFlag = false;
9411        if (turnoutBlocks[0] != null) {
9412            signalMastTurnoutPanel.add(turnoutSignalMastA.getDetailsPanel());
9413            boundaryFlag = true;
9414        }
9415        if (turnoutBlocks[1] != null) {
9416            signalMastTurnoutPanel.add(turnoutSignalMastB.getDetailsPanel());
9417            boundaryFlag = true;
9418        }
9419        if (turnoutBlocks[2] != null) {
9420            signalMastTurnoutPanel.add(turnoutSignalMastC.getDetailsPanel());
9421            boundaryFlag = true;
9422        }
9423        if (turnoutBlocks[3] != null) {
9424            signalMastTurnoutPanel.add(turnoutSignalMastD.getDetailsPanel());
9425            boundaryFlag = true;
9426        }
9427        if (!boundaryFlag) {
9428            JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("SignalsError20"));
9429        }
9430        setSignalMastsAtTurnoutFrame.setPreferredSize(null);
9431        setSignalMastsAtTurnoutFrame.pack();
9432    }   //turnoutSignalMastsGetSaved
9433
9434    private void setSignalMastsDonePressed(ActionEvent a) {
9435        //process turnout name
9436        if (!getTurnoutMastInformation()) {
9437            return;
9438        }
9439
9440        //process signal head names
9441        SignalMast turnoutMast = getSignalMastFromEntry(turnoutSignalMastA.getText(), false, setSignalsAtTurnoutFrame);
9442        SignalMast turnoutMastB = getSignalMastFromEntry(turnoutSignalMastB.getText(), false, setSignalsAtTurnoutFrame);
9443        SignalMast turnoutMastC = getSignalMastFromEntry(turnoutSignalMastC.getText(), false, setSignalsAtTurnoutFrame);
9444        SignalMast turnoutMastD = getSignalMastFromEntry(turnoutSignalMastD.getText(), false, setSignalsAtTurnoutFrame);
9445
9446        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
9447
9448        //place signals as requested
9449        if (turnoutSignalMastA.addToPanel() && (turnoutMast != null)) {
9450            if (isSignalMastOnPanel(turnoutMast)
9451                    && (turnoutMast != layoutTurnout.getSignalAMast())) {
9452                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9453                        Bundle.getMessage("SignalsError6",
9454                                new Object[]{turnoutSignalMastA.getText()}),
9455                        Bundle.getMessage("ErrorTitle"),
9456                        JmriJOptionPane.ERROR_MESSAGE);
9457                return;
9458            } else {
9459                removeSignalMastFromPanel(layoutTurnout.getSignalAMast());
9460                SignalMastIcon l = new SignalMastIcon(layoutEditor);
9461                l.setSignalMast(turnoutSignalMastA.getText());
9462                placingBlock(l, turnoutSignalMastA.isRightSelected(),
9463                                0.0, layoutTurnout.getConnectA(), layoutTurnoutView.getCoordsA());
9464                removeAssignment(turnoutMast);
9465                layoutTurnout.setSignalAMast(turnoutSignalMastA.getText());
9466                needRedraw = true;
9467            }
9468        } else if (turnoutMast != null) {
9469            LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMast, layoutTurnout);
9470            if (assigned == LayoutTurnout.Geometry.NONE) {
9471                if (isSignalMastOnPanel(turnoutMast)
9472                        && isSignalMastAssignedAnywhere(turnoutMast)) {
9473                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9474                            Bundle.getMessage("SignalsError8",
9475                                    new Object[]{turnoutSignalMastA.getText()}),
9476                            Bundle.getMessage("ErrorTitle"),
9477                            JmriJOptionPane.ERROR_MESSAGE);
9478                    return;
9479                } else {
9480                    removeSignalMastFromPanel(layoutTurnout.getSignalAMast());
9481                    removeAssignment(turnoutMast);
9482                    layoutTurnout.setSignalAMast(turnoutSignalMastA.getText());
9483                }
9484                //} else if (assigned != A1) {
9485                //need to figure out what to do in this case.
9486            }
9487        } else {
9488            removeSignalMastFromPanel(layoutTurnout.getSignalAMast());
9489            layoutTurnout.setSignalAMast("");
9490        }
9491        if ((turnoutSignalMastB.addToPanel()) && (turnoutMastB != null)) {
9492            if (isSignalMastOnPanel(turnoutMastB)
9493                    && (turnoutMastB != layoutTurnout.getSignalBMast())) {
9494                JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9495                        Bundle.getMessage("SignalsError6",
9496                                new Object[]{turnoutSignalMastB.getText()}),
9497                        Bundle.getMessage("ErrorTitle"),
9498                        JmriJOptionPane.ERROR_MESSAGE);
9499                return;
9500            } else {
9501                removeSignalMastFromPanel(layoutTurnout.getSignalBMast());
9502                SignalMastIcon l = new SignalMastIcon(layoutEditor);
9503                l.setSignalMast(turnoutSignalMastB.getText());
9504                placingBlock(l, turnoutSignalMastB.isRightSelected(),
9505                                0.0, layoutTurnout.getConnectB(), layoutTurnoutView.getCoordsB());
9506                removeAssignment(turnoutMastB);
9507                layoutTurnout.setSignalBMast(turnoutSignalMastB.getText());
9508                needRedraw = true;
9509            }
9510        } else if (turnoutMastB != null) {
9511            LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMastB, layoutTurnout);
9512            if (assigned == LayoutTurnout.Geometry.NONE) {
9513                if (isSignalMastOnPanel(turnoutMastB)
9514                        && isSignalMastAssignedAnywhere(turnoutMastB)) {
9515                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9516                            Bundle.getMessage("SignalsError8",
9517                                    new Object[]{turnoutSignalMastB.getText()}),
9518                            Bundle.getMessage("ErrorTitle"),
9519                            JmriJOptionPane.ERROR_MESSAGE);
9520                    return;
9521                } else {
9522                    removeSignalMastFromPanel(layoutTurnout.getSignalBMast());
9523                    removeAssignment(turnoutMastB);
9524                    layoutTurnout.setSignalBMast(turnoutSignalMastB.getText());
9525                }
9526                //} else if (assigned != A2) {
9527                //need to figure out what to do in this case.
9528            }
9529        } else {
9530            removeSignalMastFromPanel(layoutTurnout.getSignalBMast());
9531            layoutTurnout.setSignalBMast("");
9532        }
9533        if (turnoutMastC != null) {
9534            if (turnoutSignalMastC.addToPanel()) {
9535                if (isSignalMastOnPanel(turnoutMastC)
9536                        && (turnoutMastC != layoutTurnout.getSignalCMast())) {
9537                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9538                            Bundle.getMessage("SignalsError6",
9539                                    new Object[]{turnoutSignalMastC.getText()}),
9540                            Bundle.getMessage("ErrorTitle"),
9541                            JmriJOptionPane.ERROR_MESSAGE);
9542                    return;
9543                } else {
9544                    removeSignalMastFromPanel(layoutTurnout.getSignalCMast());
9545                    SignalMastIcon l = new SignalMastIcon(layoutEditor);
9546                    l.setSignalMast(turnoutSignalMastC.getText());
9547                    placingBlock(l, turnoutSignalMastC.isRightSelected(),
9548                                    0.0, layoutTurnout.getConnectC(), layoutTurnoutView.getCoordsC());
9549                    removeAssignment(turnoutMastC);
9550                    layoutTurnout.setSignalCMast(turnoutSignalMastC.getText());
9551                    needRedraw = true;
9552                }
9553            } else {
9554                LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMastC, layoutTurnout);
9555                if (assigned == LayoutTurnout.Geometry.NONE) {
9556                    if (isSignalMastOnPanel(turnoutMastC)
9557                            && isSignalMastAssignedAnywhere(turnoutMastC)) {
9558                        JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9559                                Bundle.getMessage("SignalsError8",
9560                                        new Object[]{turnoutSignalMastC.getText()}),
9561                                Bundle.getMessage("ErrorTitle"),
9562                                JmriJOptionPane.ERROR_MESSAGE);
9563                        return;
9564                    } else {
9565                        removeSignalMastFromPanel(layoutTurnout.getSignalCMast());
9566                        removeAssignment(turnoutMastC);
9567                        layoutTurnout.setSignalCMast(turnoutSignalMastC.getText());
9568                    }
9569                    //} else if (assigned != A3) {
9570                    //need to figure out what to do in this case.
9571                }
9572            }
9573        } else {
9574            removeSignalMastFromPanel(layoutTurnout.getSignalCMast());
9575            layoutTurnout.setSignalCMast("");
9576        }
9577        if (turnoutMastD != null) {
9578            if (turnoutSignalMastD.addToPanel()) {
9579                if (isSignalMastOnPanel(turnoutMastD)
9580                        && (turnoutMastD != layoutTurnout.getSignalDMast())) {
9581                    String signalHeadName = divergingSignalHeadComboBox.getSelectedItemDisplayName();
9582                    if (signalHeadName == null) {
9583                        signalHeadName = "";
9584                    }
9585                    JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9586                            Bundle.getMessage("SignalsError6",
9587                                    new Object[]{signalHeadName}),
9588                            Bundle.getMessage("ErrorTitle"),
9589                            JmriJOptionPane.ERROR_MESSAGE);
9590                    return;
9591                } else {
9592                    removeSignalMastFromPanel(layoutTurnout.getSignalDMast());
9593                    SignalMastIcon l = new SignalMastIcon(layoutEditor);
9594                    l.setSignalMast(turnoutSignalMastD.getText());
9595                    placingBlock(l, turnoutSignalMastD.isRightSelected(),
9596                                    0.0, layoutTurnout.getConnectD(), layoutTurnoutView.getCoordsD());
9597                    removeAssignment(turnoutMastD);
9598                    layoutTurnout.setSignalDMast(turnoutSignalMastD.getText());
9599                    needRedraw = true;
9600                }
9601            } else {
9602                LayoutTurnout.Geometry assigned = isMastAssignedHere(turnoutMastD, layoutTurnout);
9603                if (assigned == LayoutTurnout.Geometry.NONE) {
9604                    if (isSignalMastOnPanel(turnoutMastD)
9605                            && isSignalMastAssignedAnywhere(turnoutMastD)) {
9606                        JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9607                                Bundle.getMessage("SignalsError8",
9608                                        new Object[]{turnoutSignalMastD.getText()}),
9609                                Bundle.getMessage("ErrorTitle"),
9610                                JmriJOptionPane.ERROR_MESSAGE);
9611                        return;
9612                    } else {
9613                        removeSignalMastFromPanel(layoutTurnout.getSignalDMast());
9614                        removeAssignment(turnoutMastD);
9615                        layoutTurnout.setSignalDMast(turnoutSignalMastD.getText());
9616                    }
9617                    //} else if (assigned != B1) {
9618                    //need to figure out what to do in this case.
9619                }
9620            }
9621        } else {
9622            removeSignalMastFromPanel(layoutTurnout.getSignalDMast());
9623            layoutTurnout.setSignalDMast("");
9624        }
9625
9626        //make sure this layout turnout is not linked to another
9627        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
9628        layoutTurnout.setLinkedTurnoutName("");
9629        //finish up
9630        setSignalMastsAtTurnoutOpenFlag = false;
9631        setSignalMastsAtTurnoutFrame.setVisible(false);
9632        if (needRedraw) {
9633            layoutEditor.redrawPanel();
9634            needRedraw = false;
9635            layoutEditor.setDirty();
9636        }
9637    }   //setSignalMastsDonePressed
9638
9639
9640    public final Set<SignalMast> usedMasts = new HashSet<>();
9641
9642    public void createListUsedSignalMasts() {
9643        usedMasts.clear();
9644        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
9645            //We allow the same sensor to be allocated in both directions.
9646            if (po != boundary) {
9647                if (po.getEastBoundSignalMast() != null) {
9648                    usedMasts.add(po.getEastBoundSignalMast());
9649                }
9650                if (po.getWestBoundSignalMast() != null) {
9651                    usedMasts.add(po.getWestBoundSignalMast());
9652                }
9653            }
9654        }
9655
9656        for (LayoutTurnout to : layoutEditor.getLayoutTurnoutsAndSlips()) {
9657            if (to.getSignalAMast() != null) {
9658                usedMasts.add(to.getSignalAMast());
9659            }
9660            if (to.getSignalBMast() != null) {
9661                usedMasts.add(to.getSignalBMast());
9662            }
9663            if (to.getSignalCMast() != null) {
9664                usedMasts.add(to.getSignalCMast());
9665            }
9666            if (to.getSignalDMast() != null) {
9667                usedMasts.add(to.getSignalDMast());
9668            }
9669        }
9670        for (LevelXing x : layoutEditor.getLevelXings()) {
9671            if (x.getSignalAMast() != null) {
9672                usedMasts.add(x.getSignalAMast());
9673            }
9674            if (x.getSignalBMast() != null) {
9675                usedMasts.add(x.getSignalBMast());
9676            }
9677            if (x.getSignalCMast() != null) {
9678                usedMasts.add(x.getSignalCMast());
9679            }
9680            if (x.getSignalDMast() != null) {
9681                usedMasts.add(x.getSignalDMast());
9682            }
9683        }
9684        for (LayoutTurntable lt : layoutEditor.getLayoutTurntables()) {
9685            if (lt.getBufferMast() != null) {
9686                usedMasts.add(lt.getBufferMast());
9687            }
9688            if (lt.getExitSignalMast() != null) {
9689                usedMasts.add(lt.getExitSignalMast());
9690            }
9691            for (LayoutTurntable.RayTrack ray : lt.getRayTrackList()) {
9692                if (ray.getApproachMast() != null) {
9693                    usedMasts.add(ray.getApproachMast());
9694                }
9695            }
9696        }
9697        for (LayoutTraverser lt : layoutEditor.getLayoutTraversers()) {
9698            if (lt.getBufferMast() != null) {
9699                usedMasts.add(lt.getBufferMast());
9700            }
9701            if (lt.getExitSignalMast() != null) {
9702                usedMasts.add(lt.getExitSignalMast());
9703            }
9704            for (LayoutTraverser.SlotTrack slot : lt.getSlotList()) {
9705                if (slot.getApproachMast() != null) {
9706                    usedMasts.add(slot.getApproachMast());
9707                }
9708            }
9709        }
9710    }   //createListUsedSignalMasts
9711
9712    void refreshSignalMastAtTurnoutComboBox() {
9713        turnoutSignalMastsGetSaved(null);
9714        createListUsedSignalMasts();
9715
9716        usedMasts.remove(turnoutSignalMastA.getBean());
9717        usedMasts.remove(turnoutSignalMastB.getBean());
9718        usedMasts.remove(turnoutSignalMastC.getBean());
9719        usedMasts.remove(turnoutSignalMastD.getBean());
9720
9721        turnoutSignalMastA.getCombo().setExcludedItems(usedMasts);
9722        turnoutSignalMastB.getCombo().setExcludedItems(usedMasts);
9723        turnoutSignalMastC.getCombo().setExcludedItems(usedMasts);
9724        turnoutSignalMastD.getCombo().setExcludedItems(usedMasts);
9725    }
9726
9727    private LayoutTurnout.Geometry isMastAssignedHere(
9728            @CheckForNull SignalMast mast,
9729            @CheckForNull LayoutTurnout lTurnout) {
9730        if ((mast == null) || (lTurnout == null)) {
9731            return LayoutTurnout.Geometry.NONE;
9732        }
9733        String sysName = mast.getSystemName();
9734        String uName = mast.getUserName();
9735
9736        String name = lTurnout.getSignalAMastName();
9737        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9738            return LayoutTurnout.Geometry.POINTA1;
9739        }
9740        name = lTurnout.getSignalBMastName();
9741        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9742            return LayoutTurnout.Geometry.POINTA2;
9743        }
9744        name = lTurnout.getSignalCMastName();
9745        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9746            return LayoutTurnout.Geometry.POINTA3;
9747        }
9748        name = lTurnout.getSignalDMastName();
9749        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9750            return LayoutTurnout.Geometry.POINTB1;
9751        }
9752        return LayoutTurnout.Geometry.NONE;
9753    }   //isMastAssignedHere
9754
9755    public void removeAssignment(@Nonnull SignalMast mast) {
9756        String sName = mast.getSystemName();
9757        String uName = mast.getUserName();
9758        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
9759            if ((to.getSignalAMastName().equals(sName) || ((uName != null)
9760                    && (to.getSignalAMastName().equals(uName))))) {
9761                to.setSignalAMast("");
9762            }
9763            if ((to.getSignalBMastName().equals(sName) || ((uName != null)
9764                    && (to.getSignalBMastName().equals(uName))))) {
9765                to.setSignalBMast("");
9766            }
9767            if ((to.getSignalCMastName().equals(sName) || ((uName != null)
9768                    && (to.getSignalCMastName().equals(uName))))) {
9769                to.setSignalCMast("");
9770            }
9771            if ((to.getSignalDMastName().equals(sName) || ((uName != null)
9772                    && (to.getSignalDMastName().equals(uName))))) {
9773                to.setSignalDMast("");
9774            }
9775        }
9776        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
9777            if (po.getEastBoundSignalMastName().equals(sName) || po.getEastBoundSignalMastName().equals(uName)) {
9778                po.setEastBoundSignalMast("");
9779            }
9780            if (po.getWestBoundSignalMastName().equals(sName) || po.getWestBoundSignalMastName().equals(uName)) {
9781                po.setWestBoundSignalMast("");
9782            }
9783        }
9784        for (LevelXing x : layoutEditor.getLevelXings()) {
9785            if ( (x.getSignalAMastName().equals(sName) || ((uName != null)
9786                    && (x.getSignalAMastName().equals(uName))))) {
9787                x.setSignalAMast("");
9788            }
9789            if ( (x.getSignalBMastName().equals(sName) || ((uName != null)
9790                    && (x.getSignalBMastName().equals(uName))))) {
9791                x.setSignalBMast("");
9792            }
9793            if ( (x.getSignalCMastName().equals(sName) || ((uName != null)
9794                    && (x.getSignalCMastName().equals(uName))))) {
9795                x.setSignalCMast("");
9796            }
9797            if ( (x.getSignalDMastName().equals(sName) || ((uName != null)
9798                    && (x.getSignalDMastName().equals(uName))))) {
9799                x.setSignalDMast("");
9800            }
9801        }
9802    }   //removeAssignment
9803
9804    private boolean getTurnoutMastInformation() {
9805        turnout = null;
9806        layoutTurnout = null;
9807        String str = signalMastsTurnoutComboBox.getSelectedItemDisplayName();
9808        if ((str == null) || str.isEmpty()) {
9809            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame, Bundle.getMessage("SignalsError1") + "qqq",
9810                    Bundle.getMessage("ErrorTitle"),
9811                    JmriJOptionPane.ERROR_MESSAGE);
9812            return false;
9813        }
9814        turnout = InstanceManager.turnoutManagerInstance().getTurnout(str);
9815        if (turnout == null) {
9816            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9817                    Bundle.getMessage("SignalsError2",
9818                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
9819                    JmriJOptionPane.ERROR_MESSAGE);
9820            return false;
9821        } else {
9822            String uname = turnout.getUserName();
9823            if ((uname == null) || uname.isEmpty()
9824                    || !uname.equals(str)) {
9825                signalMastsTurnoutComboBox.setSelectedItem(turnout);
9826            }
9827        }
9828        layoutTurnout = layoutEditor.getFinder().findLayoutTurnoutByBean(turnout);
9829
9830        if (layoutTurnout == null) {
9831            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9832                    Bundle.getMessage("SignalsError3",
9833                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
9834                    JmriJOptionPane.ERROR_MESSAGE);
9835            return false;
9836        }
9837        return true;
9838    }
9839
9840    public void placingBlock(PositionableIcon icon, boolean isRightSide,
9841            double fromPoint, Object obj, Point2D p) {
9842        if (obj instanceof TrackSegment) {
9843            TrackSegment ts = (TrackSegment) obj;
9844            Point2D endPoint;
9845            if (ts.getConnect1() == layoutTurnout) {
9846                endPoint = layoutEditor.getCoords(ts.getConnect2(), ts.getType2());
9847            } else {
9848                endPoint = layoutEditor.getCoords(ts.getConnect1(), ts.getType1());
9849            }
9850            boolean isEast = false;
9851            if (MathUtil.equals(endPoint.getX(), p.getX())) {
9852                log.debug("X in both is the same");
9853                if (endPoint.getY() < p.getY()) {
9854                    log.debug("Y end point is less than our point");
9855                    isEast = true;
9856                }
9857            } else if (endPoint.getX() < p.getX()) {
9858                log.debug("end X point is less than our point");
9859                isEast = true;
9860            }
9861
9862            log.debug("East set is {}", isEast);
9863            setIconOnPanel(ts, icon, isEast, p, endPoint, isRightSide, fromPoint);
9864        }
9865    }
9866
9867    public void placingBlockForTurntable(PositionableIcon icon, boolean isRightSide,
9868                             double fromPoint, Object obj, Point2D p) {
9869        if (obj instanceof TrackSegment) {
9870            TrackSegment ts = (TrackSegment) obj;
9871            Point2D endPoint;
9872            // Determine the "other" end of the track segment by comparing coordinates
9873            // with the passed-in anchor point 'p'. This is more robust than checking object references.
9874            if (MathUtil.distance(layoutEditor.getCoords(ts.getConnect1(), ts.getType1()), p) < 1.0) {
9875                // connect1 is at our anchor point 'p', so the other end is connect2
9876                endPoint = layoutEditor.getCoords(ts.getConnect2(), ts.getType2());
9877            } else {
9878                // connect2 is at our anchor point 'p', so the other end is connect1
9879                endPoint = layoutEditor.getCoords(ts.getConnect1(), ts.getType1());
9880            }
9881            boolean isEast = false;
9882            if (MathUtil.equals(endPoint.getX(), p.getX())) {
9883                log.debug("X in both is the same");
9884                if (endPoint.getY() < p.getY()) {
9885                    log.debug("Y end point is less than our point");
9886                    isEast = true;
9887                }
9888            } else if (endPoint.getX() < p.getX()) {
9889                log.debug("end X point is less than our point");
9890                isEast = true;
9891            }
9892
9893            log.debug("East set is {}", isEast);
9894            setIconOnPanel(ts, icon, isEast, p, endPoint, isRightSide, fromPoint);
9895        }
9896    }
9897
9898    private void setSignalMastsCancelPressed(ActionEvent a) {
9899        setSignalMastsAtTurnoutOpenFlag = false;
9900        setSignalMastsAtTurnoutFrame.setVisible(false);
9901    }
9902
9903    /*============================*\
9904    |* setSignalMastsAtLayoutSlip *|
9905    \*============================*/
9906
9907    //operational variables for Set SignalMast at Slip tool
9908    private JmriJFrame setSignalMastsAtLayoutSlipFrame = null;
9909    private boolean setSignalMastsAtLayoutSlipOpenFlag = false;
9910    private boolean setSignalMastsAtLayoutSlipFromMenuFlag = false;
9911
9912    private JButton getSavedSlipSignalMasts = null;
9913    private JButton setSlipSignalMastsDone = null;
9914    private JButton setSlipSignalMastsCancel = null;
9915
9916    private String[] slipBlocks = new String[4];
9917
9918    private final NamedBeanComboBox<Block> slipSignalBlockAComboBox
9919            = new NamedBeanComboBox<>(
9920                    InstanceManager.getDefault(BlockManager.class),
9921                    null, DisplayOptions.DISPLAYNAME);
9922    private final NamedBeanComboBox<Block> slipSignalBlockBComboBox
9923            = new NamedBeanComboBox<>(
9924                    InstanceManager.getDefault(BlockManager.class),
9925                    null, DisplayOptions.DISPLAYNAME);
9926    private final NamedBeanComboBox<Block> slipSignalBlockCComboBox
9927            = new NamedBeanComboBox<>(
9928                    InstanceManager.getDefault(BlockManager.class),
9929                    null, DisplayOptions.DISPLAYNAME);
9930    private final NamedBeanComboBox<Block> slipSignalBlockDComboBox
9931            = new NamedBeanComboBox<>(
9932                    InstanceManager.getDefault(BlockManager.class),
9933                    null, DisplayOptions.DISPLAYNAME);
9934
9935    private JLabel slipSignalBlockANameLabel = null;
9936    private JLabel slipSignalBlockBNameLabel = null;
9937    private JLabel slipSignalBlockCNameLabel = null;
9938    private JLabel slipSignalBlockDNameLabel = null;
9939
9940    BeanDetails<SignalMast> slipSignalMastA;
9941    BeanDetails<SignalMast> slipSignalMastB;
9942    BeanDetails<SignalMast> slipSignalMastC;
9943    BeanDetails<SignalMast> slipSignalMastD;
9944
9945    JPanel signalMastLayoutSlipPanel = new JPanel(new FlowLayout());
9946
9947    public void setSignalMastsAtSlipFromMenu(@Nonnull LayoutSlip slip,
9948            @Nonnull String[] blocks, @Nonnull JFrame theFrame) {
9949        layoutSlip = slip;
9950        layoutTurnout = slip;
9951
9952        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
9953        slipSignalBlockAComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockName()));
9954        slipSignalBlockBComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockBName()));
9955        slipSignalBlockCComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockCName()));
9956        slipSignalBlockDComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockDName()));
9957
9958        slipBlocks = new String[4];
9959        for (int i = 0; i < blocks.length; i++) {
9960            slipBlocks[i] = blocks[i];
9961        }
9962        setSignalMastsAtLayoutSlipFromMenuFlag = true;
9963        setSignalMastsAtLayoutSlip(theFrame);
9964    }
9965
9966    //TODO: Add to Tools menu?
9967    public void setSignalMastsAtLayoutSlip(@Nonnull JFrame theFrame) {
9968        signalFrame = theFrame;
9969
9970        //Initialize if needed
9971        if (setSignalMastsAtLayoutSlipFrame == null) {
9972            setSignalMastsAtLayoutSlipOpenFlag = false;
9973
9974            slipSignalMastA = new BeanDetails<>("SignalMast",
9975                    InstanceManager.getDefault(SignalMastManager.class));
9976            slipSignalMastB = new BeanDetails<>("SignalMast",
9977                    InstanceManager.getDefault(SignalMastManager.class));
9978            slipSignalMastC = new BeanDetails<>("SignalMast",
9979                    InstanceManager.getDefault(SignalMastManager.class));
9980            slipSignalMastD = new BeanDetails<>("SignalMast",
9981                    InstanceManager.getDefault(SignalMastManager.class));
9982
9983            slipSignalMastA.getDetailsPanel().setBackground(new Color(255, 255, 200));
9984            slipSignalMastB.getDetailsPanel().setBackground(new Color(200, 255, 255));
9985            slipSignalMastC.getDetailsPanel().setBackground(new Color(200, 200, 255));
9986            slipSignalMastD.getDetailsPanel().setBackground(new Color(255, 200, 200));
9987
9988            setSignalMastsAtLayoutSlipFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtLayoutSlip"), false, true);
9989            oneFrameToRuleThemAll(setSignalMastsAtLayoutSlipFrame);
9990            setSignalMastsAtLayoutSlipFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
9991//         setSignalMastsAtLayoutSlipFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtLayoutSlip", true);
9992            setSignalMastsAtLayoutSlipFrame.setLocation(70, 30);
9993            Container theContentPane = setSignalMastsAtLayoutSlipFrame.getContentPane();
9994            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
9995
9996            JPanel panel11A = new JPanel(new FlowLayout());
9997//note: this is just placeholder text; real text is set below
9998            slipSignalBlockANameLabel = new JLabel(" A ");
9999            panel11A.add(slipSignalBlockANameLabel);
10000            panel11A.add(slipSignalBlockAComboBox);
10001            slipSignalBlockAComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10002            theContentPane.add(panel11A);
10003
10004            JPanel panel11B = new JPanel(new FlowLayout());
10005//note: this is just placeholder text; real text is set below
10006            slipSignalBlockBNameLabel = new JLabel(" B ");
10007            panel11B.add(slipSignalBlockBNameLabel);
10008            panel11B.add(slipSignalBlockBComboBox);
10009            slipSignalBlockBComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10010            theContentPane.add(panel11B);
10011
10012            JPanel panel11C = new JPanel(new FlowLayout());
10013//note: this is just placeholder text; real text is set below
10014            slipSignalBlockCNameLabel = new JLabel(" C ");
10015            panel11C.add(slipSignalBlockCNameLabel);
10016            panel11C.add(slipSignalBlockCComboBox);
10017            slipSignalBlockCComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10018            theContentPane.add(panel11C);
10019
10020            JPanel panel11D = new JPanel(new FlowLayout());
10021//note: this is just placeholder text; real text is set below
10022            slipSignalBlockDNameLabel = new JLabel(" D ");
10023            panel11D.add(slipSignalBlockDNameLabel);
10024            panel11D.add(slipSignalBlockDComboBox);
10025            slipSignalBlockDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10026            theContentPane.add(panel11D);
10027
10028            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10029
10030            JPanel panel2 = new JPanel(new FlowLayout());
10031            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSignalMast"));
10032            panel2.add(shTitle);
10033            panel2.add(new JLabel("   "));
10034            panel2.add(getSavedSlipSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
10035            getSavedSlipSignalMasts.addActionListener(this::slipSignalMastsGetSaved);
10036            getSavedSlipSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
10037            theContentPane.add(panel2);
10038
10039            signalMastLayoutSlipPanel.setLayout(new GridLayout(0, 2));
10040            theContentPane.add(signalMastLayoutSlipPanel);
10041            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10042
10043            JPanel panel6 = new JPanel(new FlowLayout());
10044
10045            panel6.add(new JLabel("   "));
10046            panel6.add(setSlipSignalMastsDone = new JButton(Bundle.getMessage("ButtonDone")));
10047            setSlipSignalMastsDone.addActionListener(this::setSlipSignalMastsDonePressed);
10048            setSlipSignalMastsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
10049            panel6.add(setSlipSignalMastsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
10050            setSlipSignalMastsCancel.addActionListener(this::setSlipSignalMastsCancelPressed);
10051            setSlipSignalMastsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
10052            theContentPane.add(panel6);
10053            setSignalMastsAtLayoutSlipFrame.addWindowListener(new WindowAdapter() {
10054                @Override
10055                public void windowClosing(WindowEvent e) {
10056                    setSlipSignalMastsCancelPressed(null);
10057                }
10058            });
10059        }
10060
10061        //Unhide any excluded masts
10062        slipSignalMastA.getCombo().setExcludedItems(new HashSet<>());
10063        slipSignalMastB.getCombo().setExcludedItems(new HashSet<>());
10064        slipSignalMastC.getCombo().setExcludedItems(new HashSet<>());
10065        slipSignalMastD.getCombo().setExcludedItems(new HashSet<>());
10066        signalMastLayoutSlipPanel.removeAll();
10067
10068        slipSignalBlockAComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10069        slipSignalBlockBComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10070        slipSignalBlockCComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10071        slipSignalBlockDComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10072
10073        if (setSignalMastsAtLayoutSlipFromMenuFlag) {
10074            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
10075                    Bundle.getMessage("BeanNameBlock") + " A "
10076                    + Bundle.getMessage("Name"))
10077                    + " " + layoutSlip.getBlockName());
10078            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
10079                    Bundle.getMessage("BeanNameBlock") + " B "
10080                    + Bundle.getMessage("Name"))
10081                    + " " + layoutSlip.getBlockBName());
10082            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
10083                    Bundle.getMessage("BeanNameBlock") + " C "
10084                    + Bundle.getMessage("Name"))
10085                    + " " + layoutSlip.getBlockCName());
10086            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
10087                    Bundle.getMessage("BeanNameBlock") + " D "
10088                    + Bundle.getMessage("Name"))
10089                    + " " + layoutSlip.getBlockDName());
10090            refreshSignalMastAtSlipComboBox();
10091        } else {
10092            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
10093                    Bundle.getMessage("BeanNameBlock") + " A "
10094                    + Bundle.getMessage("Name")));
10095            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
10096                    Bundle.getMessage("BeanNameBlock") + " B "
10097                    + Bundle.getMessage("Name")));
10098            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
10099                    Bundle.getMessage("BeanNameBlock") + " C "
10100                    + Bundle.getMessage("Name")));
10101            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
10102                    Bundle.getMessage("BeanNameBlock") + " D "
10103                    + Bundle.getMessage("Name")));
10104        }
10105
10106        if (!setSignalMastsAtLayoutSlipOpenFlag) {
10107            setSignalMastsAtLayoutSlipFrame.setPreferredSize(null);
10108            setSignalMastsAtLayoutSlipFrame.pack();
10109            setSignalMastsAtLayoutSlipOpenFlag = true;
10110        }
10111        setSignalMastsAtLayoutSlipFrame.setVisible(true);
10112    }
10113
10114    void refreshSignalMastAtSlipComboBox() {
10115        slipSignalMastsGetSaved(null);
10116        createListUsedSignalMasts();
10117
10118        usedMasts.remove(slipSignalMastA.getBean());
10119        usedMasts.remove(slipSignalMastB.getBean());
10120        usedMasts.remove(slipSignalMastC.getBean());
10121        usedMasts.remove(slipSignalMastD.getBean());
10122
10123        slipSignalMastA.getCombo().setExcludedItems(usedMasts);
10124        slipSignalMastB.getCombo().setExcludedItems(usedMasts);
10125        slipSignalMastC.getCombo().setExcludedItems(usedMasts);
10126        slipSignalMastD.getCombo().setExcludedItems(usedMasts);
10127    }
10128
10129    private void slipSignalMastsGetSaved(ActionEvent a) {
10130        if (!getSlipMastInformation()) {
10131            return;
10132        }
10133        slipBlocks = layoutSlip.getBlockBoundaries();
10134
10135        slipSignalMastA.setTextField(layoutSlip.getSignalAMastName());
10136        slipSignalMastB.setTextField(layoutSlip.getSignalBMastName());
10137        slipSignalMastC.setTextField(layoutSlip.getSignalCMastName());
10138        slipSignalMastD.setTextField(layoutSlip.getSignalDMastName());
10139
10140        slipSignalMastA.setBoundaryLabel(slipBlocks[0]);
10141        slipSignalMastB.setBoundaryLabel(slipBlocks[1]);
10142        slipSignalMastC.setBoundaryLabel(slipBlocks[2]);
10143        slipSignalMastD.setBoundaryLabel(slipBlocks[3]);
10144
10145        boolean boundaryFlag = false;
10146        signalMastLayoutSlipPanel.remove(slipSignalMastA.getDetailsPanel());
10147        signalMastLayoutSlipPanel.remove(slipSignalMastB.getDetailsPanel());
10148        signalMastLayoutSlipPanel.remove(slipSignalMastC.getDetailsPanel());
10149        signalMastLayoutSlipPanel.remove(slipSignalMastD.getDetailsPanel());
10150        if (slipBlocks[0] != null) {
10151            signalMastLayoutSlipPanel.add(slipSignalMastA.getDetailsPanel());
10152            boundaryFlag = true;
10153        }
10154        if (slipBlocks[1] != null) {
10155            signalMastLayoutSlipPanel.add(slipSignalMastB.getDetailsPanel());
10156            boundaryFlag = true;
10157        }
10158        if (slipBlocks[2] != null) {
10159            signalMastLayoutSlipPanel.add(slipSignalMastC.getDetailsPanel());
10160            boundaryFlag = true;
10161        }
10162        if (slipBlocks[3] != null) {
10163            signalMastLayoutSlipPanel.add(slipSignalMastD.getDetailsPanel());
10164            boundaryFlag = true;
10165        }
10166        if (!boundaryFlag) {
10167            JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame, "There are no block boundaries on this level crossing\nIt is therefore not possible to add Signal Masts to it");
10168        }
10169        setSignalMastsAtLayoutSlipFrame.setPreferredSize(null);
10170        setSignalMastsAtLayoutSlipFrame.pack();
10171    }
10172
10173    private boolean getSlipMastInformation() {
10174        if (!setSignalMastsAtLayoutSlipFromMenuFlag) {
10175            layoutSlip = null;
10176            List<LayoutSlip> layoutSlips = layoutEditor.getLayoutSlips();
10177            if (layoutSlips.size() <= 0) {
10178                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10179                        Bundle.getMessage("SignalsError15"),
10180                        Bundle.getMessage("ErrorTitle"),
10181                        JmriJOptionPane.ERROR_MESSAGE);
10182                return false;
10183            } else if (layoutSlips.size() == 1) {
10184                layoutSlip = layoutSlips.get(0);
10185            } else {
10186                LayoutBlock slipBlockA = null;
10187                //LayoutBlock slipBlockC = null;
10188                slipBlockA = getBlockFromEntry(xingBlockACComboBox);
10189                if (slipBlockA == null) {
10190                    return false;
10191                }
10192
10193                int foundCount = 0;
10194                //make two block tests first
10195                for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
10196                    LayoutBlock xA = null;
10197                    LayoutBlock xB = null;
10198                    LayoutBlock xC = null;
10199                    LayoutBlock xD = null;
10200
10201                    LayoutBlock xAC = x.getLayoutBlock();
10202                    if (x.getConnectA() != null) {
10203                        xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
10204                    }
10205                    if (x.getConnectB() != null) {
10206                        xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
10207                    }
10208                    if (x.getConnectC() != null) {
10209                        xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
10210                    }
10211                    if (x.getConnectD() != null) {
10212                        xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
10213                    }
10214                    if (((xA != null) && (xC != null) && ((xA == slipBlockA)
10215                            || (xC == slipBlockA)))
10216                            || ((xB != null) && (xD != null) && ((xB == slipBlockA)
10217                            || (xD == slipBlockA)))) {
10218                        layoutSlip = x;
10219                        foundCount++;
10220                    } else if ((xAC != null) && (xAC == slipBlockA)) {
10221                        layoutSlip = x;
10222                        foundCount++;
10223                    }
10224                }
10225                if (foundCount == 0) {
10226                    //try one block test
10227                    for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
10228                        if (slipBlockA == x.getLayoutBlock()) {
10229                            layoutSlip = x;
10230                            foundCount++;
10231                        }
10232                    }
10233                }
10234                if (foundCount > 1) {
10235                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10236                            Bundle.getMessage("SignalsError16",
10237                                    new Object[]{" " + foundCount + " "}),
10238                            Bundle.getMessage("ErrorTitle"),
10239                            JmriJOptionPane.ERROR_MESSAGE);
10240                    return false;
10241                }
10242                if (layoutSlip == null) {
10243                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10244                            Bundle.getMessage("SignalsError17"),
10245                            Bundle.getMessage("ErrorTitle"),
10246                            JmriJOptionPane.ERROR_MESSAGE);
10247                    return false;
10248                }
10249            }
10250        }
10251        return true;
10252    }
10253
10254    private void setSlipSignalMastsCancelPressed(ActionEvent a) {
10255        setSignalMastsAtLayoutSlipOpenFlag = false;
10256        setSignalMastsAtLayoutSlipFrame.setVisible(false);
10257    }
10258
10259    private void setSlipSignalMastsDonePressed(ActionEvent a) {
10260        if (!getSlipMastInformation()) {
10261            return;
10262        }
10263
10264        SignalMast aMast = getSignalMastFromEntry(slipSignalMastA.getText(), false, setSignalMastsAtLayoutSlipFrame);
10265        SignalMast bMast = getSignalMastFromEntry(slipSignalMastB.getText(), false, setSignalMastsAtLayoutSlipFrame);
10266        SignalMast cMast = getSignalMastFromEntry(slipSignalMastC.getText(), false, setSignalMastsAtLayoutSlipFrame);
10267        SignalMast dMast = getSignalMastFromEntry(slipSignalMastD.getText(), false, setSignalMastsAtLayoutSlipFrame);
10268
10269        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
10270
10271        //place or update signals as requested
10272        if ((aMast != null) && slipSignalMastA.addToPanel()) {
10273            if (isSignalMastOnPanel(aMast)
10274                    && (aMast != layoutSlip.getSignalAMast())) {
10275                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10276                        Bundle.getMessage("SignalMastsError6",
10277                                new Object[]{slipSignalMastA.getText()}),
10278                        Bundle.getMessage("ErrorTitle"),
10279                        JmriJOptionPane.ERROR_MESSAGE);
10280                return;
10281            } else {
10282                removeSignalMastFromPanel(layoutSlip.getSignalAMast());
10283                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10284                l.setSignalMast(slipSignalMastA.getText());
10285                placingBlock(l, slipSignalMastA.isRightSelected(),
10286                                        0.0, layoutSlip.getConnectA(), layoutSlipView.getCoordsA());
10287                removeAssignment(aMast);
10288                layoutSlip.setSignalAMast(slipSignalMastA.getText());
10289                needRedraw = true;
10290            }
10291        } else if ((aMast != null)
10292                && (aMast != layoutSlip.getSignalAMast())
10293                && (aMast != layoutSlip.getSignalBMast())
10294                && (aMast != layoutSlip.getSignalCMast())
10295                && (aMast != layoutSlip.getSignalDMast())) {
10296            if (isSignalMastOnPanel(aMast)) {
10297                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10298                        Bundle.getMessage("SignalMastsError13",
10299                                new Object[]{slipSignalMastA.getText()}),
10300                        Bundle.getMessage("ErrorTitle"),
10301                        JmriJOptionPane.ERROR_MESSAGE);
10302                return;
10303            } else {
10304                removeSignalMastFromPanel(layoutSlip.getSignalAMast());
10305                removeAssignment(aMast);
10306                layoutSlip.setSignalAMast(slipSignalMastA.getText());
10307            }
10308        } else if ((aMast != null)
10309                && ((aMast == layoutSlip.getSignalBMast())
10310                || (aMast == layoutSlip.getSignalCMast())
10311                || (aMast == layoutSlip.getSignalDMast()))) {
10312            //need to figure out what to do in this case.
10313            log.trace("need to figure out what to do in this case.");
10314        } else if (aMast == null) {
10315            removeSignalMastFromPanel(layoutSlip.getSignalAMast());
10316            layoutSlip.setSignalAMast("");
10317        }
10318        if ((bMast != null) && slipSignalMastB.addToPanel()) {
10319            if (isSignalMastOnPanel(bMast)
10320                    && (bMast != layoutSlip.getSignalBMast())) {
10321                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10322                        Bundle.getMessage("SignalMastsError6",
10323                                new Object[]{slipSignalMastB.getText()}),
10324                        Bundle.getMessage("ErrorTitle"),
10325                        JmriJOptionPane.ERROR_MESSAGE);
10326                return;
10327            } else {
10328                removeSignalMastFromPanel(layoutSlip.getSignalBMast());
10329                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10330                l.setSignalMast(slipSignalMastB.getText());
10331                placingBlock(l, slipSignalMastB.isRightSelected(),
10332                                            0.0, layoutSlip.getConnectB(), layoutSlipView.getCoordsB());
10333                removeAssignment(bMast);
10334                layoutSlip.setSignalBMast(slipSignalMastB.getText());
10335                needRedraw = true;
10336            }
10337        } else if ((bMast != null)
10338                && (bMast != layoutSlip.getSignalAMast())
10339                && (bMast != layoutSlip.getSignalBMast())
10340                && (bMast != layoutSlip.getSignalCMast())
10341                && (bMast != layoutSlip.getSignalDMast())) {
10342            if (isSignalMastOnPanel(bMast)) {
10343                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10344                        Bundle.getMessage("SignalMastsError13",
10345                                new Object[]{slipSignalMastB.getText()}),
10346                        Bundle.getMessage("ErrorTitle"),
10347                        JmriJOptionPane.ERROR_MESSAGE);
10348                return;
10349            } else {
10350                removeSignalMastFromPanel(layoutSlip.getSignalBMast());
10351                removeAssignment(bMast);
10352                layoutSlip.setSignalBMast(slipSignalMastB.getText());
10353            }
10354        } else if ((bMast != null)
10355                && ((bMast == layoutSlip.getSignalAMast())
10356                || (bMast == layoutSlip.getSignalCMast())
10357                || (bMast == layoutSlip.getSignalDMast()))) {
10358            //need to figure out what to do in this case.
10359            log.trace("need to figure out what to do in this case.");
10360        } else if (bMast == null) {
10361            removeSignalMastFromPanel(layoutSlip.getSignalBMast());
10362            layoutSlip.setSignalBMast("");
10363        }
10364        if ((cMast != null) && slipSignalMastC.addToPanel()) {
10365            if (isSignalMastOnPanel(cMast)
10366                    && (cMast != layoutSlip.getSignalCMast())) {
10367                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10368                        Bundle.getMessage("SignalMastsError6",
10369                                new Object[]{slipSignalMastC.getText()}),
10370                        Bundle.getMessage("ErrorTitle"),
10371                        JmriJOptionPane.ERROR_MESSAGE);
10372                return;
10373            } else {
10374                removeSignalMastFromPanel(layoutSlip.getSignalCMast());
10375                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10376                l.setSignalMast(slipSignalMastC.getText());
10377                placingBlock(l, slipSignalMastA.isRightSelected(),
10378                                                0.0, layoutSlip.getConnectC(), layoutSlipView.getCoordsC());
10379                removeAssignment(cMast);
10380                layoutSlip.setSignalCMast(slipSignalMastC.getText());
10381                needRedraw = true;
10382            }
10383        } else if ((cMast != null)
10384                && (cMast != layoutSlip.getSignalAMast())
10385                && (cMast != layoutSlip.getSignalBMast())
10386                && (cMast != layoutSlip.getSignalCMast())
10387                && (cMast != layoutSlip.getSignalDMast())) {
10388            if (isSignalMastOnPanel(cMast)) {
10389                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10390                        Bundle.getMessage("SignalMastsError13",
10391                                new Object[]{slipSignalMastC.getText()}),
10392                        Bundle.getMessage("ErrorTitle"),
10393                        JmriJOptionPane.ERROR_MESSAGE);
10394                return;
10395            } else {
10396                removeSignalMastFromPanel(layoutSlip.getSignalCMast());
10397                removeAssignment(cMast);
10398                layoutSlip.setSignalCMast(slipSignalMastC.getText());
10399            }
10400        } else if ((cMast != null)
10401                && ((cMast == layoutSlip.getSignalBMast())
10402                || (cMast == layoutSlip.getSignalAMast())
10403                || (cMast == layoutSlip.getSignalDMast()))) {
10404            //need to figure out what to do in this case.
10405            log.trace("need to figure out what to do in this case.");
10406        } else if (cMast == null) {
10407            removeSignalMastFromPanel(layoutSlip.getSignalCMast());
10408            layoutSlip.setSignalCMast("");
10409        }
10410        if ((dMast != null) && slipSignalMastD.addToPanel()) {
10411            if (isSignalMastOnPanel(dMast)
10412                    && (dMast != layoutSlip.getSignalDMast())) {
10413                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10414                        Bundle.getMessage("SignalMastsError6",
10415                                new Object[]{slipSignalMastD.getText()}),
10416                        Bundle.getMessage("ErrorTitle"),
10417                        JmriJOptionPane.ERROR_MESSAGE);
10418                return;
10419            } else {
10420                removeSignalMastFromPanel(layoutSlip.getSignalDMast());
10421                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10422                l.setSignalMast(slipSignalMastD.getText());
10423                placingBlock(l, slipSignalMastD.isRightSelected(),
10424                                                0.0, layoutSlip.getConnectD(), layoutSlipView.getCoordsD());
10425                removeAssignment(dMast);
10426                layoutSlip.setSignalDMast(slipSignalMastD.getText());
10427                needRedraw = true;
10428            }
10429        } else if ((dMast != null)
10430                && (dMast != layoutSlip.getSignalAMast())
10431                && (dMast != layoutSlip.getSignalBMast())
10432                && (dMast != layoutSlip.getSignalCMast())
10433                && (dMast != layoutSlip.getSignalDMast())) {
10434            if (isSignalMastOnPanel(dMast)) {
10435                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10436                        Bundle.getMessage("SignalMastsError13",
10437                                new Object[]{slipSignalMastD.getText()}),
10438                        Bundle.getMessage("ErrorTitle"),
10439                        JmriJOptionPane.ERROR_MESSAGE);
10440                return;
10441            } else {
10442                removeSignalMastFromPanel(layoutSlip.getSignalDMast());
10443                removeAssignment(dMast);
10444                layoutSlip.setSignalDMast(slipSignalMastD.getText());
10445            }
10446        } else if ((dMast != null)
10447                && ((dMast == layoutSlip.getSignalBMast())
10448                || (dMast == layoutSlip.getSignalCMast())
10449                || (dMast == layoutSlip.getSignalAMast()))) {
10450            //need to figure out what to do in this case.
10451            log.trace("need to figure out what to do in this case.");
10452        } else if (dMast == null) {
10453            removeSignalMastFromPanel(layoutSlip.getSignalDMast());
10454            layoutSlip.setSignalDMast("");
10455        }
10456        //setup logic if requested
10457        //finish up
10458        setSignalMastsAtLayoutSlipOpenFlag = false;
10459        setSignalMastsAtLayoutSlipFrame.setVisible(false);
10460        if (needRedraw) {
10461            layoutEditor.redrawPanel();
10462            needRedraw = false;
10463            layoutEditor.setDirty();
10464        }
10465    }
10466
10467    /*===========================*\
10468    |* setSignalMastsAtLevelXing *|
10469    \*===========================*/
10470    //operational variables for Set SignalMast at Level Crossing tool
10471    private JmriJFrame setSignalMastsAtLevelXingFrame = null;
10472    private boolean setSignalMastsAtLevelXingOpenFlag = false;
10473    private boolean setSignalMastsAtLevelXingFromMenuFlag = false;
10474
10475    private JLabel xingSignalBlockACNameLabel = null;
10476    private JLabel xingSignalBlockBDNameLabel = null;
10477
10478    private final NamedBeanComboBox<Block> xingBlockACComboBox = new NamedBeanComboBox<>(
10479            InstanceManager.getDefault(BlockManager.class),
10480            null, DisplayOptions.DISPLAYNAME);
10481    private final NamedBeanComboBox<Block> xingBlockBDComboBox = new NamedBeanComboBox<>(
10482            InstanceManager.getDefault(BlockManager.class),
10483            null, DisplayOptions.DISPLAYNAME);
10484
10485    private JButton getSavedXingSignalMasts = null;
10486    private JButton setXingSignalMastsDone = null;
10487    private JButton setXingSignalMastsCancel = null;
10488
10489    private String[] xingBlocks = new String[4];
10490
10491    BeanDetails<SignalMast> xingSignalMastA;
10492    BeanDetails<SignalMast> xingSignalMastB;
10493    BeanDetails<SignalMast> xingSignalMastC;
10494    BeanDetails<SignalMast> xingSignalMastD;
10495
10496    JPanel signalMastLevelXingPanel = new JPanel(new FlowLayout());
10497
10498    Border blackline = BorderFactory.createLineBorder(Color.black);
10499
10500    //display dialog for Set Signals at Level Crossing tool
10501    public void setSignalMastsAtLevelXingFromMenu(@Nonnull LevelXing xing,
10502            @Nonnull String[] blocks,
10503            @Nonnull JFrame theFrame) {
10504        levelXing = xing;
10505        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
10506        xingBlockACComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameAC()));
10507        xingBlockBDComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameBD()));
10508        xingBlocks = new String[4];
10509        for (int i = 0; i < blocks.length; i++) {
10510            xingBlocks[i] = blocks[i];
10511        }
10512        setSignalMastsAtLevelXingFromMenuFlag = true;
10513        setSignalMastsAtLevelXing(theFrame);
10514        setSignalMastsAtLevelXingFromMenuFlag = false;
10515    }
10516
10517    //TODO: Add to Tools menu?
10518    public void setSignalMastsAtLevelXing(@Nonnull JFrame theFrame) {
10519        signalFrame = theFrame;
10520
10521        //Initialize if needed
10522        if (setSignalMastsAtLevelXingFrame == null) {
10523            setSignalMastsAtLevelXingOpenFlag = false;
10524
10525            xingSignalMastA = new BeanDetails<>("SignalMast",
10526                    InstanceManager.getDefault(SignalMastManager.class
10527                    ));
10528            xingSignalMastB = new BeanDetails<>("SignalMast",
10529                    InstanceManager.getDefault(SignalMastManager.class
10530                    ));
10531            xingSignalMastC = new BeanDetails<>("SignalMast",
10532                    InstanceManager.getDefault(SignalMastManager.class
10533                    ));
10534            xingSignalMastD = new BeanDetails<>("SignalMast",
10535                    InstanceManager.getDefault(SignalMastManager.class
10536                    ));
10537
10538            xingSignalMastA.getDetailsPanel().setBackground(new Color(255, 255, 200));
10539            xingSignalMastB.getDetailsPanel().setBackground(new Color(200, 255, 255));
10540            xingSignalMastC.getDetailsPanel().setBackground(new Color(200, 200, 255));
10541            xingSignalMastD.getDetailsPanel().setBackground(new Color(255, 200, 200));
10542
10543            setSignalMastsAtLevelXingFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtLevelXing"), false, true);
10544            oneFrameToRuleThemAll(setSignalMastsAtLevelXingFrame);
10545            setSignalMastsAtLevelXingFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
10546            setSignalMastsAtLevelXingFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtLevelXing", true);
10547            setSignalMastsAtLevelXingFrame.setLocation(70, 30);
10548            Container theContentPane = setSignalMastsAtLevelXingFrame.getContentPane();
10549            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
10550
10551            JPanel panel11 = new JPanel(new FlowLayout());
10552            xingSignalBlockACNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
10553                    Bundle.getMessage("BeanNameBlock") + " AC "
10554                    + Bundle.getMessage("Name")));
10555            panel11.add(xingSignalBlockACNameLabel);
10556            panel11.add(xingBlockACComboBox);
10557            xingBlockACComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10558            theContentPane.add(panel11);
10559
10560            JPanel panel12 = new JPanel(new FlowLayout());
10561            xingSignalBlockBDNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
10562                    Bundle.getMessage("BeanNameBlock") + " BD "
10563                    + Bundle.getMessage("Name")));
10564            panel12.add(xingSignalBlockBDNameLabel);
10565            panel12.add(xingBlockBDComboBox);
10566            xingBlockBDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10567            theContentPane.add(panel12);
10568            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10569
10570            JPanel panel2 = new JPanel(new FlowLayout());
10571            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSignalMast"));
10572            panel2.add(shTitle);
10573            panel2.add(new JLabel("   "));
10574            panel2.add(getSavedXingSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
10575            getSavedXingSignalMasts.addActionListener(this::xingSignalMastsGetSaved);
10576            getSavedXingSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
10577            theContentPane.add(panel2);
10578
10579            signalMastLevelXingPanel.setLayout(new GridLayout(0, 2));
10580
10581            theContentPane.add(signalMastLevelXingPanel);
10582            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10583
10584            JPanel panel6 = new JPanel(new FlowLayout());
10585
10586            panel6.add(new JLabel("   "));
10587            panel6.add(setXingSignalMastsDone = new JButton(Bundle.getMessage("ButtonDone")));
10588            setXingSignalMastsDone.addActionListener(this::setXingSignalMastsDonePressed);
10589            setXingSignalMastsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
10590            panel6.add(setXingSignalMastsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
10591            setXingSignalMastsCancel.addActionListener(this::setXingSignalMastsCancelPressed);
10592            setXingSignalMastsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
10593            theContentPane.add(panel6);
10594            setSignalMastsAtLevelXingFrame.addWindowListener(new WindowAdapter() {
10595                @Override
10596                public void windowClosing(WindowEvent e) {
10597                    setXingSignalMastsCancelPressed(null);
10598                }
10599            });
10600        } //if (setSignalMastsAtLevelXingFrame == null)
10601
10602        //Unhide any excluded masts
10603        xingSignalMastA.getCombo().setExcludedItems(new HashSet<>());
10604        xingSignalMastB.getCombo().setExcludedItems(new HashSet<>());
10605        xingSignalMastC.getCombo().setExcludedItems(new HashSet<>());
10606        xingSignalMastD.getCombo().setExcludedItems(new HashSet<>());
10607        signalMastLevelXingPanel.removeAll();
10608
10609        if (setSignalMastsAtLevelXingFromMenuFlag) {
10610            xingBlockACComboBox.setVisible(false);
10611            xingBlockBDComboBox.setVisible(false);
10612
10613            xingSignalBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
10614                    (Bundle.getMessage("BeanNameBlock") + " AC"))
10615                    + " " + levelXing.getBlockNameAC());
10616            xingSignalBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
10617                    (Bundle.getMessage("BeanNameBlock") + " BD"))
10618                    + " " + levelXing.getBlockNameBD());
10619
10620            xingSignalMastA.setTextField(levelXing.getSignalAMastName());
10621            xingSignalMastB.setTextField(levelXing.getSignalBMastName());
10622            xingSignalMastC.setTextField(levelXing.getSignalCMastName());
10623            xingSignalMastD.setTextField(levelXing.getSignalDMastName());
10624
10625            xingSignalMastsGetSaved(null);
10626            refreshSignalMastAtXingComboBox();
10627        } else {
10628            xingSignalBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
10629                    Bundle.getMessage("BeanNameBlock") + " AC "
10630                    + Bundle.getMessage("Name")));
10631            xingSignalBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
10632                    Bundle.getMessage("BeanNameBlock") + " BD "
10633                    + Bundle.getMessage("Name")));
10634        }
10635
10636        if (!setSignalMastsAtLevelXingOpenFlag) {
10637            setSignalMastsAtLevelXingFrame.setPreferredSize(null);
10638            setSignalMastsAtLevelXingFrame.pack();
10639            setSignalMastsAtLevelXingOpenFlag = true;
10640        }
10641        setSignalMastsAtLevelXingFrame.setVisible(true);
10642    }   //setSignalMastsAtLevelXing
10643
10644    void refreshSignalMastAtXingComboBox() {
10645        xingSignalMastsGetSaved(null);
10646        createListUsedSignalMasts();
10647
10648        usedMasts.remove(xingSignalMastA.getBean());
10649        usedMasts.remove(xingSignalMastB.getBean());
10650        usedMasts.remove(xingSignalMastC.getBean());
10651        usedMasts.remove(xingSignalMastD.getBean());
10652
10653        xingSignalMastA.getCombo().setExcludedItems(usedMasts);
10654        xingSignalMastB.getCombo().setExcludedItems(usedMasts);
10655        xingSignalMastC.getCombo().setExcludedItems(usedMasts);
10656        xingSignalMastD.getCombo().setExcludedItems(usedMasts);
10657    }
10658
10659    private void xingSignalMastsGetSaved(ActionEvent a) {
10660        if (!getLevelCrossingMastInformation()) {
10661            return;
10662        }
10663        xingBlocks = levelXing.getBlockBoundaries();
10664
10665        xingSignalMastA.setTextField(levelXing.getSignalAMastName());
10666        xingSignalMastB.setTextField(levelXing.getSignalBMastName());
10667        xingSignalMastC.setTextField(levelXing.getSignalCMastName());
10668        xingSignalMastD.setTextField(levelXing.getSignalDMastName());
10669
10670        xingSignalMastA.setBoundaryLabel(xingBlocks[0]);
10671        xingSignalMastB.setBoundaryLabel(xingBlocks[1]);
10672        xingSignalMastC.setBoundaryLabel(xingBlocks[2]);
10673        xingSignalMastD.setBoundaryLabel(xingBlocks[3]);
10674
10675        boolean boundaryFlag = false;
10676        signalMastLevelXingPanel.remove(xingSignalMastA.getDetailsPanel());
10677        signalMastLevelXingPanel.remove(xingSignalMastB.getDetailsPanel());
10678        signalMastLevelXingPanel.remove(xingSignalMastC.getDetailsPanel());
10679        signalMastLevelXingPanel.remove(xingSignalMastD.getDetailsPanel());
10680        if (xingBlocks[0] != null) {
10681            signalMastLevelXingPanel.add(xingSignalMastA.getDetailsPanel());
10682            boundaryFlag = true;
10683        }
10684        if (xingBlocks[1] != null) {
10685            signalMastLevelXingPanel.add(xingSignalMastB.getDetailsPanel());
10686            boundaryFlag = true;
10687        }
10688        if (xingBlocks[2] != null) {
10689            signalMastLevelXingPanel.add(xingSignalMastC.getDetailsPanel());
10690            boundaryFlag = true;
10691        }
10692        if (xingBlocks[3] != null) {
10693            signalMastLevelXingPanel.add(xingSignalMastD.getDetailsPanel());
10694            boundaryFlag = true;
10695        }
10696        if (!boundaryFlag) {
10697            JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame, "There are no block boundaries on this level crossing\nIt is therefore not possible to add Signal Masts to it");
10698        }
10699        setSignalMastsAtLevelXingFrame.setPreferredSize(null);
10700        setSignalMastsAtLevelXingFrame.pack();
10701    }   //xingSignalMastsGetSaved
10702
10703    private boolean getLevelCrossingMastInformation() {
10704        if (!setSignalMastsAtLevelXingFromMenuFlag) {
10705            levelXing = null;
10706            List<LevelXing> levelXings = layoutEditor.getLevelXings();
10707            if (levelXings.size() <= 0) {
10708                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10709                        Bundle.getMessage("SignalsError15"),
10710                        Bundle.getMessage("ErrorTitle"),
10711                        JmriJOptionPane.ERROR_MESSAGE);
10712                return false;
10713            } else if (levelXings.size() == 1) {
10714                levelXing = levelXings.get(0);
10715            } else {
10716                LayoutBlock xingBlockA = null;
10717                LayoutBlock xingBlockC = null;
10718                xingBlockA = getBlockFromEntry(xingBlockACComboBox);
10719                if (xingBlockA == null) {
10720                    return false;
10721                }
10722
10723                String theBlockName = xingBlockBDComboBox.getSelectedItemDisplayName();
10724                if ((theBlockName != null) && !theBlockName.isEmpty()) {
10725                    xingBlockC = getBlockFromEntry(xingBlockBDComboBox);
10726                    if (xingBlockC == null) {
10727                        return false;
10728                    }
10729                }
10730
10731                int foundCount = 0;
10732                //make two block tests first
10733                if (xingBlockC != null) {
10734                    for (LevelXing x : layoutEditor.getLevelXings()) {
10735                        LayoutBlock xA = null;
10736                        LayoutBlock xB = null;
10737                        LayoutBlock xC = null;
10738                        LayoutBlock xD = null;
10739                        LayoutBlock xAC = x.getLayoutBlockAC();
10740                        LayoutBlock xBD = x.getLayoutBlockBD();
10741                        if (x.getConnectA() != null) {
10742                            xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
10743                        }
10744                        if (x.getConnectB() != null) {
10745                            xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
10746                        }
10747                        if (x.getConnectC() != null) {
10748                            xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
10749                        }
10750                        if (x.getConnectD() != null) {
10751                            xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
10752                        }
10753                        if (((xA != null) && (xC != null) && (((xA == xingBlockA) && (xC == xingBlockC))
10754                                || ((xA == xingBlockC) && (xC == xingBlockA))))
10755                                || ((xB != null) && (xD != null) && (((xB == xingBlockA) && (xD == xingBlockC))
10756                                || ((xB == xingBlockC) && (xD == xingBlockA))))) {
10757                            levelXing = x;
10758                            foundCount++;
10759                        } else if ((xAC != null) && (xBD != null) && (((xAC == xingBlockA) && (xBD == xingBlockC))
10760                                || ((xAC == xingBlockC) && (xBD == xingBlockA)))) {
10761                            levelXing = x;
10762                            foundCount++;
10763                        }
10764                    }
10765                }
10766                if (foundCount == 0) {
10767                    //try one block test
10768                    for (LevelXing x : layoutEditor.getLevelXings()) {
10769                        if ((xingBlockA == x.getLayoutBlockAC()) || (xingBlockA == x.getLayoutBlockBD())) {
10770                            levelXing = x;
10771                            foundCount++;
10772                        }
10773                    }
10774                }
10775                if (foundCount > 1) {
10776                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10777                            Bundle.getMessage("SignalsError16",
10778                                    new Object[]{" " + foundCount + " "}),
10779                            Bundle.getMessage("ErrorTitle"),
10780                            JmriJOptionPane.ERROR_MESSAGE);
10781                    return false;
10782                }
10783                if (levelXing == null) {
10784                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10785                            Bundle.getMessage("SignalsError17"),
10786                            Bundle.getMessage("ErrorTitle"),
10787                            JmriJOptionPane.ERROR_MESSAGE);
10788                    return false;
10789                }
10790            }
10791        }
10792        return true;
10793    }   //getLevelCrossingMastInformation
10794
10795    private void setXingSignalMastsCancelPressed(ActionEvent a) {
10796        setSignalMastsAtLevelXingOpenFlag = false;
10797        setSignalMastsAtLevelXingFrame.setVisible(false);
10798    }
10799
10800    private void setXingSignalMastsDonePressed(ActionEvent a) {
10801        if (!getLevelCrossingMastInformation()) {
10802            return;
10803        }
10804        SignalMast aMast = getSignalMastFromEntry(xingSignalMastA.getText(), false, setSignalMastsAtLevelXingFrame);
10805        SignalMast bMast = getSignalMastFromEntry(xingSignalMastB.getText(), false, setSignalMastsAtLevelXingFrame);
10806        SignalMast cMast = getSignalMastFromEntry(xingSignalMastC.getText(), false, setSignalMastsAtLevelXingFrame);
10807        SignalMast dMast = getSignalMastFromEntry(xingSignalMastD.getText(), false, setSignalMastsAtLevelXingFrame);
10808
10809        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
10810
10811        //if ( !getXingSignalMastInformation() ) return;
10812        //place or update signals as requested
10813        if ((aMast != null) && xingSignalMastA.addToPanel()) {
10814            if (isSignalMastOnPanel(aMast)
10815                    && (aMast != levelXing.getSignalAMast())) {
10816                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10817                        Bundle.getMessage("SignalMastsError6",
10818                                new Object[]{xingSignalMastA.getText()}),
10819                        Bundle.getMessage("ErrorTitle"),
10820                        JmriJOptionPane.ERROR_MESSAGE);
10821                return;
10822            } else {
10823                removeSignalMastFromPanel(levelXing.getSignalAMast());
10824                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10825                l.setSignalMast(xingSignalMastA.getText());
10826                placingBlock(l, xingSignalMastA.isRightSelected(), 0.0, levelXing.getConnectA(), levelXingView.getCoordsA());
10827                removeAssignment(aMast);
10828                levelXing.setSignalAMast(xingSignalMastA.getText());
10829                needRedraw = true;
10830            }
10831        } else if ((aMast != null)
10832                && (aMast != levelXing.getSignalAMast())
10833                && (aMast != levelXing.getSignalBMast())
10834                && (aMast != levelXing.getSignalCMast())
10835                && (aMast != levelXing.getSignalDMast())) {
10836            if (isSignalMastOnPanel(aMast)) {
10837                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10838                        Bundle.getMessage("SignalMastsError13",
10839                                new Object[]{xingSignalMastA.getText()}),
10840                        Bundle.getMessage("ErrorTitle"),
10841                        JmriJOptionPane.ERROR_MESSAGE);
10842                return;
10843            } else {
10844                removeSignalMastFromPanel(levelXing.getSignalAMast());
10845                removeAssignment(aMast);
10846                levelXing.setSignalAMast(xingSignalMastA.getText());
10847            }
10848        } else if ((aMast != null)
10849                && ((aMast == levelXing.getSignalBMast())
10850                || (aMast == levelXing.getSignalCMast())
10851                || (aMast == levelXing.getSignalDMast()))) {
10852            //need to figure out what to do in this case.
10853            log.trace("need to figure out what to do in this case.");
10854        } else if (aMast == null) {
10855            removeSignalMastFromPanel(levelXing.getSignalAMast());
10856            levelXing.setSignalAMast("");
10857        }
10858        if ((bMast != null) && xingSignalMastB.addToPanel()) {
10859            if (isSignalMastOnPanel(bMast)
10860                    && (bMast != levelXing.getSignalBMast())) {
10861                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10862                        Bundle.getMessage("SignalMastsError6",
10863                                new Object[]{xingSignalMastB.getText()}),
10864                        Bundle.getMessage("ErrorTitle"),
10865                        JmriJOptionPane.ERROR_MESSAGE);
10866                return;
10867            } else {
10868                removeSignalMastFromPanel(levelXing.getSignalBMast());
10869                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10870                l.setSignalMast(xingSignalMastB.getText());
10871                placingBlock(l, xingSignalMastB.isRightSelected(), 0.0, levelXing.getConnectB(), levelXingView.getCoordsB());
10872                removeAssignment(bMast);
10873                levelXing.setSignalBMast(xingSignalMastB.getText());
10874                needRedraw = true;
10875            }
10876        } else if ((bMast != null)
10877                && (bMast != levelXing.getSignalAMast())
10878                && (bMast != levelXing.getSignalBMast())
10879                && (bMast != levelXing.getSignalCMast())
10880                && (bMast != levelXing.getSignalDMast())) {
10881            if (isSignalMastOnPanel(bMast)) {
10882                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10883                        Bundle.getMessage("SignalMastsError13",
10884                                new Object[]{xingSignalMastB.getText()}),
10885                        Bundle.getMessage("ErrorTitle"),
10886                        JmriJOptionPane.ERROR_MESSAGE);
10887                return;
10888            } else {
10889                removeSignalMastFromPanel(levelXing.getSignalBMast());
10890                removeAssignment(bMast);
10891                levelXing.setSignalBMast(xingSignalMastB.getText());
10892            }
10893        } else if ((bMast != null)
10894                && ((bMast == levelXing.getSignalAMast())
10895                || (bMast == levelXing.getSignalCMast())
10896                || (bMast == levelXing.getSignalBMast())
10897                || (bMast == levelXing.getSignalDMast()))) {
10898            //need to figure out what to do in this case.
10899            log.trace("need to figure out what to do in this case.");
10900        } else if (bMast == null) {
10901            removeSignalMastFromPanel(levelXing.getSignalBMast());
10902            levelXing.setSignalBMast("");
10903        }
10904        if ((cMast != null) && xingSignalMastC.addToPanel()) {
10905            if (isSignalMastOnPanel(cMast)
10906                    && (cMast != levelXing.getSignalCMast())) {
10907                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10908                        Bundle.getMessage("SignalMastsError6",
10909                                new Object[]{xingSignalMastC.getText()}),
10910                        Bundle.getMessage("ErrorTitle"),
10911                        JmriJOptionPane.ERROR_MESSAGE);
10912                return;
10913            } else {
10914                removeSignalMastFromPanel(levelXing.getSignalCMast());
10915                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10916                l.setSignalMast(xingSignalMastC.getText());
10917                placingBlock(l, xingSignalMastC.isRightSelected(), 0.0, levelXing.getConnectC(), levelXingView.getCoordsC());
10918                removeAssignment(cMast);
10919                levelXing.setSignalCMast(xingSignalMastC.getText());
10920                needRedraw = true;
10921            }
10922        } else if ((cMast != null)
10923                && (cMast != levelXing.getSignalAMast())
10924                && (cMast != levelXing.getSignalBMast())
10925                && (cMast != levelXing.getSignalCMast())
10926                && (cMast != levelXing.getSignalDMast())) {
10927            if (isSignalMastOnPanel(cMast)) {
10928                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10929                        Bundle.getMessage("SignalMastsError13",
10930                                new Object[]{xingSignalMastC.getText()}),
10931                        Bundle.getMessage("ErrorTitle"),
10932                        JmriJOptionPane.ERROR_MESSAGE);
10933                return;
10934            } else {
10935                removeSignalMastFromPanel(levelXing.getSignalCMast());
10936                removeAssignment(cMast);
10937                levelXing.setSignalCMast(xingSignalMastC.getText());
10938            }
10939        } else if ((cMast != null)
10940                && ((cMast == levelXing.getSignalBMast())
10941                || (cMast == levelXing.getSignalAMast())
10942                || (cMast == levelXing.getSignalDMast()))) {
10943            //need to figure out what to do in this case.
10944            log.trace("need to figure out what to do in this case.");
10945        } else if (cMast == null) {
10946            removeSignalMastFromPanel(levelXing.getSignalCMast());
10947            levelXing.setSignalCName("");
10948        }
10949        if ((dMast != null) && xingSignalMastD.addToPanel()) {
10950            if (isSignalMastOnPanel(dMast)
10951                    && (dMast != levelXing.getSignalDMast())) {
10952                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10953                        Bundle.getMessage("SignalMastsError6",
10954                                new Object[]{xingSignalMastD.getText()}),
10955                        Bundle.getMessage("ErrorTitle"),
10956                        JmriJOptionPane.ERROR_MESSAGE);
10957                return;
10958            } else {
10959                removeSignalMastFromPanel(levelXing.getSignalDMast());
10960                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10961                l.setSignalMast(xingSignalMastD.getText());
10962                placingBlock(l, xingSignalMastD.isRightSelected(), 0.0, levelXing.getConnectD(), levelXingView.getCoordsD());
10963                removeAssignment(dMast);
10964                levelXing.setSignalDMast(xingSignalMastD.getText());
10965                needRedraw = true;
10966            }
10967        } else if ((dMast != null)
10968                && (dMast != levelXing.getSignalAMast())
10969                && (dMast != levelXing.getSignalBMast())
10970                && (dMast != levelXing.getSignalCMast())
10971                && (dMast != levelXing.getSignalDMast())) {
10972            if (isSignalMastOnPanel(dMast)) {
10973                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10974                        Bundle.getMessage("SignalMastsError13",
10975                                new Object[]{xingSignalMastD.getText()}),
10976                        Bundle.getMessage("ErrorTitle"),
10977                        JmriJOptionPane.ERROR_MESSAGE);
10978                return;
10979            } else {
10980                removeSignalMastFromPanel(levelXing.getSignalDMast());
10981                removeAssignment(dMast);
10982                levelXing.setSignalDMast(xingSignalMastD.getText());
10983            }
10984        } else if ((dMast != null)
10985                && ((dMast == levelXing.getSignalBMast())
10986                || (dMast == levelXing.getSignalCMast())
10987                || (dMast == levelXing.getSignalAMast()))) {
10988            //need to figure out what to do in this case.
10989            log.trace("need to figure out what to do in this case.");
10990        } else if (dMast == null) {
10991            removeSignalMastFromPanel(levelXing.getSignalDMast());
10992            levelXing.setSignalDMast("");
10993        }
10994        //setup logic if requested
10995        //finish up
10996        setSignalMastsAtLevelXingOpenFlag = false;
10997        setSignalMastsAtLevelXingFrame.setVisible(false);
10998        if (needRedraw) {
10999            layoutEditor.redrawPanel();
11000            needRedraw = false;
11001            layoutEditor.setDirty();
11002        }
11003    }   //setXingSignalMastsDonePressed
11004
11005    /*=====================*\
11006    |* setSensorsAtTurnout *|
11007    \*=====================*/
11008    private JmriJFrame setSensorsAtTurnoutFrame = null;
11009    private boolean setSensorsAtTurnoutOpenFlag = false;
11010    private boolean setSensorsAtTurnoutFromMenuFlag = false;
11011
11012    private JFrame turnoutSensorFrame = null;
11013    private JLabel turnoutSensorNameLabel = null;
11014
11015    private final NamedBeanComboBox<Turnout> sensorsTurnoutComboBox
11016            = new NamedBeanComboBox<>(
11017                    InstanceManager.turnoutManagerInstance(),
11018                    null, DisplayOptions.DISPLAYNAME);
11019
11020    private JButton setSensorsDone;
11021    private JButton getSavedSensors;
11022    private JButton setSensorsCancel;
11023    private JButton changeSensorIcon = null;
11024
11025    private String[] turnoutSenBlocks = new String[4];
11026
11027    BeanDetails<Sensor> turnoutSensorA;
11028    BeanDetails<Sensor> turnoutSensorB;
11029    BeanDetails<Sensor> turnoutSensorC;
11030    BeanDetails<Sensor> turnoutSensorD;
11031
11032    JPanel sensorTurnoutPanel = new JPanel(new FlowLayout());
11033
11034    public void setSensorsAtTurnoutFromMenu(@Nonnull LayoutTurnout to,
11035            @Nonnull String[] blocks,
11036            @Nonnull MultiIconEditor theEditor,
11037            @Nonnull JFrame frame) {
11038        sensorIconEditor = theEditor;
11039        layoutTurnout = to;
11040        turnout = to.getTurnout();
11041        sensorsTurnoutComboBox.setSelectedItem(turnout);
11042        turnoutSenBlocks = new String[4];
11043        for (int i = 0; i < blocks.length; i++) {
11044            turnoutSenBlocks[i] = blocks[i];
11045        }
11046        setSensorsAtTurnoutFromMenuFlag = true;
11047        setSensorsAtTurnout(frame);
11048        setSensorsAtTurnoutFromMenuFlag = false;
11049    }
11050
11051    //TODO: Add to Tools menu?
11052    public void setSensorsAtTurnout(@Nonnull JFrame frame) {
11053        turnoutSensorFrame = frame;
11054
11055        //Initialize if needed
11056        if (setSensorsAtTurnoutFrame == null) {
11057            setSensorsAtTurnoutOpenFlag = false;
11058
11059            turnoutSensorA = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11060            turnoutSensorB = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11061            turnoutSensorC = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11062            turnoutSensorD = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11063
11064            turnoutSensorA.getDetailsPanel().setBackground(new Color(255, 255, 200));
11065            turnoutSensorB.getDetailsPanel().setBackground(new Color(200, 255, 255));
11066            turnoutSensorC.getDetailsPanel().setBackground(new Color(200, 200, 255));
11067            turnoutSensorD.getDetailsPanel().setBackground(new Color(255, 200, 200));
11068
11069            setSensorsAtTurnoutFrame = new JmriJFrame(Bundle.getMessage("SensorsAtTurnout"), false, true);
11070            oneFrameToRuleThemAll(setSensorsAtTurnoutFrame);
11071            setSensorsAtTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
11072//         setSensorsAtTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtTurnout", true);
11073            setSensorsAtTurnoutFrame.setLocation(70, 30);
11074            Container theContentPane = setSensorsAtTurnoutFrame.getContentPane();
11075            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
11076
11077            JPanel panel1 = new JPanel(new FlowLayout());
11078
11079            turnoutSensorNameLabel = new JLabel(Bundle.getMessage("BeanNameTurnout") + " "
11080                    + Bundle.getMessage("Name"));
11081            panel1.add(turnoutSensorNameLabel);
11082            panel1.add(sensorsTurnoutComboBox);
11083            sensorsTurnoutComboBox.setToolTipText(Bundle.getMessage("SensorsTurnoutNameHint"));
11084
11085            theContentPane.add(panel1);
11086            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11087
11088            JPanel panel2 = new JPanel(new FlowLayout());
11089            JLabel shTitle = new JLabel(Bundle.getMessage("Sensors"));
11090            panel2.add(shTitle);
11091            panel2.add(new JLabel("   "));
11092            panel2.add(getSavedSensors = new JButton(Bundle.getMessage("GetSaved")));
11093            getSavedSensors.addActionListener(this::turnoutSensorsGetSaved);
11094            getSavedSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
11095            theContentPane.add(panel2);
11096
11097            sensorTurnoutPanel.setLayout(new GridLayout(0, 2)); //Content added as needed
11098            theContentPane.add(sensorTurnoutPanel);
11099
11100            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11101
11102            JPanel panel6 = new JPanel(new FlowLayout());
11103            panel6.add(changeSensorIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
11104            changeSensorIcon.addActionListener((ActionEvent e) -> turnoutSensorFrame.setVisible(true));
11105            changeSensorIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
11106            panel6.add(new JLabel("   "));
11107            panel6.add(setSensorsDone = new JButton(Bundle.getMessage("ButtonDone")));
11108            setSensorsDone.addActionListener(this::setSensorsDonePressed);
11109            setSensorsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
11110            panel6.add(setSensorsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
11111            setSensorsCancel.addActionListener(this::setSensorsCancelPressed);
11112            setSensorsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
11113            theContentPane.add(panel6);
11114            setSensorsAtTurnoutFrame.addWindowListener(new WindowAdapter() {
11115                @Override
11116                public void windowClosing(WindowEvent e) {
11117                    setSensorsCancelPressed(null);
11118                }
11119            });
11120        }
11121
11122        sensorTurnoutPanel.removeAll();
11123
11124        sensorsTurnoutComboBox.setVisible(!setSensorsAtTurnoutFromMenuFlag);
11125
11126        if (setSensorsAtTurnoutFromMenuFlag) {
11127            turnoutSensorNameLabel.setText(Bundle.getMessage("MakeLabel",
11128                    Bundle.getMessage("BeanNameTurnout")
11129                    + " " + Bundle.getMessage("Name"))
11130                    + " " + layoutTurnout.getTurnoutName());
11131            turnoutSensorsGetSaved(null);
11132        } else {
11133            turnoutSensorNameLabel.setText(Bundle.getMessage("BeanNameTurnout") + " "
11134                    + Bundle.getMessage("Name"));
11135        }
11136
11137        if (!setSensorsAtTurnoutOpenFlag) {
11138            setSensorsAtTurnoutFrame.setPreferredSize(null);
11139            setSensorsAtTurnoutFrame.pack();
11140            setSensorsAtTurnoutOpenFlag = true;
11141        }
11142        setSensorsAtTurnoutFrame.setVisible(true);
11143    }   //setSensorsAtTurnout
11144
11145    private void turnoutSensorsGetSaved(ActionEvent a) {
11146        if (!getTurnoutSensorInformation()) {
11147            return;
11148        }
11149        turnoutSenBlocks = layoutTurnout.getBlockBoundaries();
11150
11151        turnoutSensorA.setTextField(layoutTurnout.getSensorAName());
11152        turnoutSensorB.setTextField(layoutTurnout.getSensorBName());
11153        turnoutSensorC.setTextField(layoutTurnout.getSensorCName());
11154        turnoutSensorD.setTextField(layoutTurnout.getSensorDName());
11155
11156        turnoutSensorA.setBoundaryLabel(turnoutSenBlocks[0]);
11157        turnoutSensorB.setBoundaryLabel(turnoutSenBlocks[1]);
11158        turnoutSensorC.setBoundaryLabel(turnoutSenBlocks[2]);
11159        turnoutSensorD.setBoundaryLabel(turnoutSenBlocks[3]);
11160
11161        sensorTurnoutPanel.remove(turnoutSensorA.getDetailsPanel());
11162        sensorTurnoutPanel.remove(turnoutSensorB.getDetailsPanel());
11163        sensorTurnoutPanel.remove(turnoutSensorC.getDetailsPanel());
11164        sensorTurnoutPanel.remove(turnoutSensorD.getDetailsPanel());
11165
11166        boolean boundaryFlag = false;
11167        if (turnoutSenBlocks[0] != null) {
11168            sensorTurnoutPanel.add(turnoutSensorA.getDetailsPanel());
11169            boundaryFlag = true;
11170        }
11171        if (turnoutSenBlocks[1] != null) {
11172            sensorTurnoutPanel.add(turnoutSensorB.getDetailsPanel());
11173            boundaryFlag = true;
11174        }
11175        if (turnoutSenBlocks[2] != null) {
11176            sensorTurnoutPanel.add(turnoutSensorC.getDetailsPanel());
11177            boundaryFlag = true;
11178        }
11179        if (turnoutSenBlocks[3] != null) {
11180            sensorTurnoutPanel.add(turnoutSensorD.getDetailsPanel());
11181            boundaryFlag = true;
11182        }
11183        if (!boundaryFlag) {
11184            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame, "There are no block boundaries on this turnout\nIt is therefore not possible to add Sensors to it");
11185        }
11186        setSensorsAtTurnoutFrame.setPreferredSize(null);
11187        setSensorsAtTurnoutFrame.pack();
11188    }   //turnoutSensorsGetSaved
11189
11190    SensorIcon turnoutSensorBlockIcon;
11191
11192    private void setSensorsDonePressed(ActionEvent a) {
11193        log.trace("setSensorsDonePressed (turnouts)");  // NOI18N
11194        if (!getTurnoutSensorInformation()) {
11195            return;
11196        }
11197
11198        //process sensor names
11199        Sensor sensorA = getSensorFromEntry(turnoutSensorA.getText(), false, setSensorsAtTurnoutFrame);
11200        Sensor sensorB = getSensorFromEntry(turnoutSensorB.getText(), false, setSensorsAtTurnoutFrame);
11201        Sensor sensorC = getSensorFromEntry(turnoutSensorC.getText(), false, setSensorsAtTurnoutFrame);
11202        Sensor sensorD = getSensorFromEntry(turnoutSensorD.getText(), false, setSensorsAtTurnoutFrame);
11203
11204        Sensor currSensorA = layoutTurnout.getSensorA();
11205        Sensor currSensorB = layoutTurnout.getSensorB();
11206        Sensor currSensorC = layoutTurnout.getSensorC();
11207        Sensor currSensorD = layoutTurnout.getSensorD();
11208
11209        if (log.isTraceEnabled()) {
11210            log.trace("current sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11211                    (currSensorA == null) ? "- none- " : currSensorA.getDisplayName(), // NOI18N
11212                    (currSensorB == null) ? "- none- " : currSensorB.getDisplayName(), // NOI18N
11213                    (currSensorC == null) ? "- none- " : currSensorC.getDisplayName(), // NOI18N
11214                    (currSensorD == null) ? "- none- " : currSensorD.getDisplayName());  // NOI18N
11215            log.trace("new sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11216                    (sensorA == null) ? "- none- " : sensorA.getDisplayName(), // NOI18N
11217                    (sensorB == null) ? "- none- " : sensorB.getDisplayName(), // NOI18N
11218                    (sensorC == null) ? "- none- " : sensorC.getDisplayName(), // NOI18N
11219                    (sensorD == null) ? "- none- " : sensorD.getDisplayName());  // NOI18N
11220        }
11221
11222        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
11223
11224        //place/remove sensors as requested
11225        if (sensorA == null) {
11226            if (currSensorA != null && removeSensorFromPanel(currSensorA)) {
11227                layoutTurnout.setSensorA(null);
11228            }
11229        } else if (turnoutSensorA != null && layoutTurnout.getConnectA() != null) {
11230            setTurnoutSensor(layoutTurnout, sensorA, currSensorA, turnoutSensorA, layoutTurnout.getConnectA(), layoutTurnoutView.getCoordsA(), "A");
11231        }
11232
11233        if (sensorB == null) {
11234            if (currSensorB != null && removeSensorFromPanel(currSensorB)) {
11235                layoutTurnout.setSensorB(null);
11236            }
11237        } else if (turnoutSensorB != null && layoutTurnout.getConnectB() != null) {
11238            setTurnoutSensor(layoutTurnout, sensorB, currSensorB, turnoutSensorB, layoutTurnout.getConnectB(), layoutTurnoutView.getCoordsB(), "B");
11239        }
11240
11241        if (sensorC == null) {
11242            if (currSensorC != null && removeSensorFromPanel(currSensorC)) {
11243                layoutTurnout.setSensorC(null);
11244            }
11245        } else if (turnoutSensorC != null && layoutTurnout.getConnectC() != null) {
11246            setTurnoutSensor(layoutTurnout, sensorC, currSensorC, turnoutSensorC, layoutTurnout.getConnectC(), layoutTurnoutView.getCoordsC(), "C");
11247        }
11248
11249        if (sensorD == null) {
11250            if (currSensorD != null && removeSensorFromPanel(currSensorD)) {
11251                layoutTurnout.setSensorD(null);
11252            }
11253        } else if (turnoutSensorD != null && layoutTurnout.getConnectD() != null) {
11254            setTurnoutSensor(layoutTurnout, sensorD, currSensorD, turnoutSensorD, layoutTurnout.getConnectD(), layoutTurnoutView.getCoordsD(), "D");
11255        }
11256
11257        //make sure this layout turnout is not linked to another
11258        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
11259        layoutTurnout.setLinkedTurnoutName("");
11260
11261        //finish up
11262        setSensorsAtTurnoutOpenFlag = false;
11263        setSensorsAtTurnoutFrame.setVisible(false);
11264        if (needRedraw) {
11265            layoutEditor.redrawPanel();
11266            needRedraw = false;
11267            layoutEditor.setDirty();
11268        }
11269    }   //setSensorsDonePressed
11270
11271    /**
11272     * Attached a sensor to a turnout block boundary. Supports both
11273     * LayoutTurnout and LayoutSlip classes.
11274     *
11275     * @since 4.11.2
11276     * @param <T>        The specific type, a subtype of LayoutTurnout
11277     * @param trackItem  The turnout or slip that is being modified.
11278     * @param newSensor  The sensor that is being added.
11279     * @param currSensor The sensor that might already be there, otherwise null.
11280     * @param beanDetail The BeanDetails object that contains the supporting
11281     *                   data.
11282     * @param connect    The track segment that is attached to this point
11283     * @param coords     The track componennt coordinates
11284     * @param position   Which of the four points is being changed
11285     */
11286    <T extends LayoutTurnout> void setTurnoutSensor(T trackItem, Sensor newSensor, Sensor currSensor,
11287            BeanDetails<? extends NamedBean> beanDetail, LayoutTrack connect, Point2D coords, String position) {
11288        if (currSensor == null) {
11289            if (!isSensorAssignedAnywhere(newSensor)) {
11290                log.trace("Add sensor '{}'", newSensor.getDisplayName());  // NOI18N
11291                switch (position) {
11292                    case "A":  // NOI18N
11293                        trackItem.setSensorA(beanDetail.getText());
11294                        break;
11295                    case "B":  // NOI18N
11296                        trackItem.setSensorB(beanDetail.getText());
11297                        break;
11298                    case "C":  // NOI18N
11299                        trackItem.setSensorC(beanDetail.getText());
11300                        break;
11301                    case "D":  // NOI18N
11302                        trackItem.setSensorD(beanDetail.getText());
11303                        break;
11304                    default:
11305                        break;
11306                }
11307                if (beanDetail.addToPanel()) {
11308                    log.trace("Add icon for sensor '{}'", newSensor.getDisplayName());  // NOI18N
11309                    placingBlock(getSensorIcon(beanDetail.getText()),
11310                            beanDetail.isRightSelected(), 0.0,
11311                            connect, coords);
11312                    needRedraw = true;
11313                }
11314            } else {
11315                sensorAssignedElseWhere(newSensor);
11316            }
11317        } else if (currSensor == newSensor) {
11318            if (beanDetail.addToPanel()) {
11319                if (!isSensorOnPanel(newSensor)) {
11320                    log.trace("Add icon for existing sensor '{}'", newSensor.getDisplayName());  // NOI18N
11321                    placingBlock(getSensorIcon(beanDetail.getText()),
11322                            beanDetail.isRightSelected(), 0.0,
11323                            connect, coords);
11324                    needRedraw = true;
11325                }
11326            }
11327        } else {
11328            if (!isSensorAssignedAnywhere(newSensor)) {
11329                if (removeSensorFromPanel(currSensor)) {
11330                    log.trace("Replace sensor '{}' with sensor '{}'", // NOI18N
11331                            currSensor.getDisplayName(), newSensor.getDisplayName());
11332                    switch (position) {
11333                        case "A":  // NOI18N
11334                            trackItem.setSensorA(beanDetail.getText());
11335                            break;
11336                        case "B":  // NOI18N
11337                            trackItem.setSensorB(beanDetail.getText());
11338                            break;
11339                        case "C":  // NOI18N
11340                            trackItem.setSensorC(beanDetail.getText());
11341                            break;
11342                        case "D":  // NOI18N
11343                            trackItem.setSensorD(beanDetail.getText());
11344                            break;
11345                        default:
11346                            break;
11347                    }
11348                    if (beanDetail.addToPanel()) {
11349                        log.trace("Add icon for replacement sensor '{}'", // NOI18N
11350                                newSensor.getDisplayName());
11351                        placingBlock(getSensorIcon(beanDetail.getText()),
11352                                beanDetail.isRightSelected(), 0.0,
11353                                connect, coords);
11354                        needRedraw = true;
11355                    }
11356                }
11357            } else {
11358                sensorAssignedElseWhere(newSensor);
11359            }
11360        }
11361    }
11362
11363    private boolean getTurnoutSensorInformation() {
11364        turnout = null;
11365        layoutTurnout = null;
11366        String str = sensorsTurnoutComboBox.getSelectedItemDisplayName();
11367        if ((str == null) || str.isEmpty()) {
11368            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame, Bundle.getMessage("SensorsError1"),
11369                    Bundle.getMessage("ErrorTitle"),
11370                    JmriJOptionPane.ERROR_MESSAGE);
11371            return false;
11372        }
11373        turnout = InstanceManager.turnoutManagerInstance().getTurnout(str);
11374        if (turnout == null) {
11375            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame,
11376                    Bundle.getMessage("SensorsError2",
11377                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
11378                    JmriJOptionPane.ERROR_MESSAGE);
11379            return false;
11380        } else {
11381            String uname = turnout.getUserName();
11382            if ((uname == null) || uname.isEmpty()
11383                    || !uname.equals(str)) {
11384                sensorsTurnoutComboBox.setSelectedItem(turnout);
11385            }
11386        }
11387        layoutTurnout = layoutEditor.getFinder().findLayoutTurnoutByBean(turnout);
11388        if (layoutTurnout == null) {
11389            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame,
11390                    Bundle.getMessage("SensorsError3",
11391                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
11392                    JmriJOptionPane.ERROR_MESSAGE);
11393            return false;
11394        }
11395        return true;
11396    }   //getTurnoutSensorInformation
11397
11398    private void setSensorsCancelPressed(ActionEvent a) {
11399        setSensorsAtTurnoutOpenFlag = false;
11400        setSensorsAtTurnoutFrame.setVisible(false);
11401    }
11402
11403    /*=======================*\
11404    |* setSensorsAtLevelXing *|
11405    \*=======================*/
11406    //operational variables for Set Sensors at Level Crossing tool
11407    private JmriJFrame setSensorsAtLevelXingFrame = null;
11408    private boolean setSensorsAtLevelXingOpenFlag = false;
11409    private boolean setSensorsAtLevelXingFromMenuFlag = false;
11410
11411    private JLabel xingSensorsBlockACNameLabel = null;
11412    private JLabel xingSensorsBlockBDNameLabel = null;
11413
11414    private final NamedBeanComboBox<Block> xingSensorsBlockACComboBox
11415            = new NamedBeanComboBox<>(
11416                    InstanceManager.getDefault(BlockManager.class),
11417                    null, DisplayOptions.DISPLAYNAME);
11418    private final NamedBeanComboBox<Block> xingSensorsBlockBDComboBox
11419            = new NamedBeanComboBox<>(
11420                    InstanceManager.getDefault(BlockManager.class),
11421                    null, DisplayOptions.DISPLAYNAME);
11422
11423    private JButton getSavedXingSensors = null;
11424    private JButton setXingSensorsDone = null;
11425    private JButton setXingSensorsCancel = null;
11426    private JButton changeSensorXingIcon = null;
11427    JFrame sensorXingFrame = null;
11428
11429    private String[] xingSensorBlocks = new String[4];
11430
11431    BeanDetails<Sensor> xingSensorA;
11432    BeanDetails<Sensor> xingSensorB;
11433    BeanDetails<Sensor> xingSensorC;
11434    BeanDetails<Sensor> xingSensorD;
11435
11436    JPanel sensorXingPanel = new JPanel(new FlowLayout());
11437
11438    //display dialog for Set Signals at Level Crossing tool
11439    public void setSensorsAtLevelXingFromMenu(@Nonnull LevelXing xing,
11440            @Nonnull String[] blocks,
11441            @Nonnull MultiIconEditor theEditor,
11442            @Nonnull JFrame theFrame) {
11443        levelXing = xing;
11444        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
11445        xingSensorsBlockACComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameAC()));
11446        xingSensorsBlockBDComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameBD()));
11447        for (int i = 0; i < blocks.length; i++) {
11448            xingSensorBlocks[i] = blocks[i];
11449        }
11450        setSensorsAtLevelXingFromMenuFlag = true;
11451        setSensorsAtLevelXing(theEditor, theFrame);
11452        setSensorsAtLevelXingFromMenuFlag = false;
11453    }
11454
11455    //TODO: Add to Tools menu?
11456    public void setSensorsAtLevelXing(@Nonnull MultiIconEditor theEditor,
11457            @Nonnull JFrame theFrame) {
11458        sensorIconEditor = theEditor;
11459        sensorXingFrame = theFrame;
11460
11461        //Initialize if needed
11462        if (setSensorsAtLevelXingFrame == null) {
11463            setSensorsAtLevelXingOpenFlag = false;
11464
11465            xingSensorA = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11466            xingSensorB = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11467            xingSensorC = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11468            xingSensorD = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11469
11470            xingSensorA.getDetailsPanel().setBackground(new Color(255, 255, 200));
11471            xingSensorB.getDetailsPanel().setBackground(new Color(200, 255, 255));
11472            xingSensorC.getDetailsPanel().setBackground(new Color(200, 200, 255));
11473            xingSensorD.getDetailsPanel().setBackground(new Color(255, 200, 200));
11474
11475            setSensorsAtLevelXingFrame = new JmriJFrame(Bundle.getMessage("SensorsAtLevelXing"), false, true);
11476            oneFrameToRuleThemAll(setSensorsAtLevelXingFrame);
11477            setSensorsAtLevelXingFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
11478//         setSensorsAtLevelXingFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtLevelXing", true);
11479            setSensorsAtLevelXingFrame.setLocation(70, 30);
11480            Container theContentPane = setSensorsAtLevelXingFrame.getContentPane();
11481            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
11482
11483            JPanel panel11 = new JPanel(new FlowLayout());
11484
11485            xingSensorsBlockACNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
11486                    Bundle.getMessage("BeanNameBlock") + " AC "
11487                    + Bundle.getMessage("Name")));
11488            panel11.add(xingSensorsBlockACNameLabel);
11489            panel11.add(xingSensorsBlockACComboBox);
11490            xingSensorsBlockACComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
11491            theContentPane.add(panel11);
11492
11493            JPanel panel12 = new JPanel(new FlowLayout());
11494            xingSensorsBlockBDNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
11495                    Bundle.getMessage("BeanNameBlock") + " BD "
11496                    + Bundle.getMessage("Name")));
11497            panel12.add(xingSensorsBlockBDNameLabel);
11498            panel12.add(xingSensorsBlockBDComboBox);
11499            xingSensorsBlockBDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
11500            theContentPane.add(panel12);
11501
11502            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11503
11504            JPanel panel2 = new JPanel(new FlowLayout());
11505            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSensor"));
11506            panel2.add(shTitle);
11507            panel2.add(new JLabel("   "));
11508            panel2.add(getSavedXingSensors = new JButton(Bundle.getMessage("GetSaved")));
11509            getSavedXingSensors.addActionListener(this::xingSensorsGetSaved);
11510            getSavedXingSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
11511            theContentPane.add(panel2);
11512
11513            sensorXingPanel.setLayout(new GridLayout(0, 2));
11514            theContentPane.add(sensorXingPanel);
11515            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11516
11517            JPanel panel6 = new JPanel(new FlowLayout());
11518            panel6.add(changeSensorXingIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
11519            changeSensorXingIcon.addActionListener((ActionEvent e) -> sensorXingFrame.setVisible(true));
11520            changeSensorXingIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
11521
11522            panel6.add(new JLabel("   "));
11523            panel6.add(setXingSensorsDone = new JButton(Bundle.getMessage("ButtonDone")));
11524            setXingSensorsDone.addActionListener(this::setXingSensorsDonePressed);
11525            setXingSensorsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
11526            panel6.add(setXingSensorsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
11527            setXingSensorsCancel.addActionListener(this::setXingSensorsCancelPressed);
11528            setXingSensorsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
11529            theContentPane.add(panel6);
11530            setSensorsAtLevelXingFrame.addWindowListener(new WindowAdapter() {
11531                @Override
11532                public void windowClosing(WindowEvent e) {
11533                    setXingSensorsCancelPressed(null);
11534                }
11535            });
11536        }
11537
11538        sensorXingPanel.removeAll();
11539
11540        xingSensorsBlockACComboBox.setVisible(!setSensorsAtLevelXingFromMenuFlag);
11541        xingSensorsBlockBDComboBox.setVisible(!setSensorsAtLevelXingFromMenuFlag);
11542
11543        if (setSensorsAtLevelXingFromMenuFlag) {
11544            xingSensorsBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
11545                    Bundle.getMessage("BeanNameBlock") + " AC "
11546                    + Bundle.getMessage("Name")) + " " + levelXing.getBlockNameAC());
11547            xingSensorsBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
11548                    Bundle.getMessage("BeanNameBlock") + " BD "
11549                    + Bundle.getMessage("Name")) + " " + levelXing.getBlockNameBD());
11550
11551            xingSensorA.setTextField(levelXing.getSensorAName());
11552            xingSensorB.setTextField(levelXing.getSensorBName());
11553            xingSensorC.setTextField(levelXing.getSensorCName());
11554            xingSensorD.setTextField(levelXing.getSensorDName());
11555            xingSensorsGetSaved(null);
11556        } else {
11557            xingSensorsBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
11558                    Bundle.getMessage("BeanNameBlock") + " AC "
11559                    + Bundle.getMessage("Name")));
11560            xingSensorsBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
11561                    Bundle.getMessage("BeanNameBlock") + " BD "
11562                    + Bundle.getMessage("Name")));
11563        }
11564
11565        if (!setSensorsAtLevelXingOpenFlag) {
11566            setSensorsAtLevelXingFrame.setPreferredSize(null);
11567            setSensorsAtLevelXingFrame.pack();
11568            setSensorsAtLevelXingOpenFlag = true;
11569        }
11570        setSensorsAtLevelXingFrame.setVisible(true);
11571    }
11572
11573    private void xingSensorsGetSaved(ActionEvent a) {
11574        if (!getLevelCrossingSensorInformation()) {
11575            return;
11576        }
11577
11578        xingSensorBlocks = levelXing.getBlockBoundaries();
11579
11580        xingSensorA.setTextField(levelXing.getSensorAName());
11581        xingSensorB.setTextField(levelXing.getSensorBName());
11582        xingSensorC.setTextField(levelXing.getSensorCName());
11583        xingSensorD.setTextField(levelXing.getSensorDName());
11584
11585        sensorXingPanel.remove(xingSensorA.getDetailsPanel());
11586        sensorXingPanel.remove(xingSensorB.getDetailsPanel());
11587        sensorXingPanel.remove(xingSensorC.getDetailsPanel());
11588        sensorXingPanel.remove(xingSensorD.getDetailsPanel());
11589
11590        xingSensorA.setBoundaryLabel(xingSensorBlocks[0]);
11591        xingSensorB.setBoundaryLabel(xingSensorBlocks[1]);
11592        xingSensorC.setBoundaryLabel(xingSensorBlocks[2]);
11593        xingSensorD.setBoundaryLabel(xingSensorBlocks[3]);
11594
11595        boolean boundaryFlag = false;
11596        if (xingSensorBlocks[0] != null) {
11597            sensorXingPanel.add(xingSensorA.getDetailsPanel());
11598            boundaryFlag = true;
11599        }
11600        if (xingSensorBlocks[1] != null) {
11601            sensorXingPanel.add(xingSensorB.getDetailsPanel());
11602            boundaryFlag = true;
11603        }
11604        if (xingSensorBlocks[2] != null) {
11605            sensorXingPanel.add(xingSensorC.getDetailsPanel());
11606            boundaryFlag = true;
11607        }
11608        if (xingSensorBlocks[3] != null) {
11609            sensorXingPanel.add(xingSensorD.getDetailsPanel());
11610            boundaryFlag = true;
11611        }
11612        if (!boundaryFlag) {
11613            JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame, Bundle.getMessage("NoBoundaryXingSensor"));
11614        }
11615        setSensorsAtLevelXingFrame.setPreferredSize(null);
11616        setSensorsAtLevelXingFrame.pack();
11617    }
11618
11619    private boolean getLevelCrossingSensorInformation() {
11620        if (!setSensorsAtLevelXingFromMenuFlag) {
11621            levelXing = null;
11622            List<LevelXing> levelXings = layoutEditor.getLevelXings();
11623            if (levelXings.size() <= 0) {
11624                JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame,
11625                        Bundle.getMessage("SignalsError15"),
11626                        Bundle.getMessage("ErrorTitle"),
11627                        JmriJOptionPane.ERROR_MESSAGE);
11628                return false;
11629            } else if (levelXings.size() == 1) {
11630                levelXing = levelXings.get(0);
11631            } else {
11632                LayoutBlock xingSensorBlockA = null;
11633                LayoutBlock xingSensorBlockC = null;
11634                xingSensorBlockA = getBlockFromEntry(xingSensorsBlockACComboBox);
11635                if (xingSensorBlockA == null) {
11636                    return false;
11637                }
11638                String theBlockName = xingSensorsBlockBDComboBox.getSelectedItemDisplayName();
11639                if ((theBlockName != null) && !theBlockName.isEmpty()) {
11640                    xingSensorBlockC = getBlockFromEntry(xingSensorsBlockBDComboBox);
11641                    if (xingSensorBlockC == null) {
11642                        return false;
11643                    }
11644                }
11645
11646                int foundCount = 0;
11647                //make two block tests first
11648                if (xingSensorBlockC != null) {
11649                    for (LevelXing x : layoutEditor.getLevelXings()) {
11650                        LayoutBlock xA = null;
11651                        LayoutBlock xB = null;
11652                        LayoutBlock xC = null;
11653                        LayoutBlock xD = null;
11654                        LayoutBlock xAC = x.getLayoutBlockAC();
11655                        LayoutBlock xBD = x.getLayoutBlockBD();
11656                        if (x.getConnectA() != null) {
11657                            xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
11658                        }
11659                        if (x.getConnectB() != null) {
11660                            xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
11661                        }
11662                        if (x.getConnectC() != null) {
11663                            xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
11664                        }
11665                        if (x.getConnectD() != null) {
11666                            xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
11667                        }
11668                        if (((xA != null) && (xC != null) && (((xA == xingSensorBlockA) && (xC == xingSensorBlockC))
11669                                || ((xA == xingSensorBlockC) && (xC == xingSensorBlockA))))
11670                                || ((xB != null) && (xD != null) && (((xB == xingSensorBlockA) && (xD == xingSensorBlockC))
11671                                || ((xB == xingSensorBlockC) && (xD == xingSensorBlockA))))) {
11672                            levelXing = x;
11673                            foundCount++;
11674                        } else if ((xAC != null) && (xBD != null) && (((xAC == xingSensorBlockA) && (xBD == xingSensorBlockC))
11675                                || ((xAC == xingSensorBlockC) && (xBD == xingSensorBlockA)))) {
11676                            levelXing = x;
11677                            foundCount++;
11678                        }
11679                    }
11680                }
11681                if (foundCount == 0) {
11682                    //try one block test
11683                    for (LevelXing x : layoutEditor.getLevelXings()) {
11684                        if ((xingSensorBlockA == x.getLayoutBlockAC()) || (xingSensorBlockA == x.getLayoutBlockBD())) {
11685                            levelXing = x;
11686                            foundCount++;
11687                        }
11688                    }
11689                }
11690                if (foundCount > 1) {
11691                    JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame,
11692                            Bundle.getMessage("SignalsError16",
11693                                    new Object[]{" " + foundCount + " "}),
11694                            Bundle.getMessage("ErrorTitle"),
11695                            JmriJOptionPane.ERROR_MESSAGE);
11696                    return false;
11697                }
11698                if (levelXing == null) {
11699                    JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame,
11700                            Bundle.getMessage("SignalsError17"),
11701                            Bundle.getMessage("ErrorTitle"),
11702                            JmriJOptionPane.ERROR_MESSAGE);
11703                    return false;
11704                }
11705            }
11706        }
11707        return true;
11708    }
11709
11710    private void setXingSensorsCancelPressed(ActionEvent a) {
11711        setSensorsAtLevelXingOpenFlag = false;
11712        setSensorsAtLevelXingFrame.setVisible(false);
11713    }
11714
11715    private void setXingSensorsDonePressed(ActionEvent a) {
11716        log.trace("setXingSensorsDonePressed");  // NOI18N
11717
11718        if (!getLevelCrossingSensorInformation()) {
11719            return;
11720        }
11721
11722        Sensor aSensor = getSensorFromEntry(xingSensorA.getText(), false, setSensorsAtLevelXingFrame);
11723        Sensor bSensor = getSensorFromEntry(xingSensorB.getText(), false, setSensorsAtLevelXingFrame);
11724        Sensor cSensor = getSensorFromEntry(xingSensorC.getText(), false, setSensorsAtLevelXingFrame);
11725        Sensor dSensor = getSensorFromEntry(xingSensorD.getText(), false, setSensorsAtLevelXingFrame);
11726
11727        Sensor currSensorA = levelXing.getSensorA();
11728        Sensor currSensorB = levelXing.getSensorB();
11729        Sensor currSensorC = levelXing.getSensorC();
11730        Sensor currSensorD = levelXing.getSensorD();
11731
11732        if (log.isTraceEnabled()) {
11733            log.trace("current sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11734                    (currSensorA == null) ? "- none- " : currSensorA.getDisplayName(), // NOI18N
11735                    (currSensorB == null) ? "- none- " : currSensorB.getDisplayName(), // NOI18N
11736                    (currSensorC == null) ? "- none- " : currSensorC.getDisplayName(), // NOI18N
11737                    (currSensorD == null) ? "- none- " : currSensorD.getDisplayName());  // NOI18N
11738            log.trace("new sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11739                    (aSensor == null) ? "- none- " : aSensor.getDisplayName(), // NOI18N
11740                    (bSensor == null) ? "- none- " : bSensor.getDisplayName(), // NOI18N
11741                    (cSensor == null) ? "- none- " : cSensor.getDisplayName(), // NOI18N
11742                    (dSensor == null) ? "- none- " : dSensor.getDisplayName());  // NOI18N
11743        }
11744
11745        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
11746        // place/remove sensors as requested
11747        if (aSensor == null) {
11748            if (currSensorA != null && removeSensorFromPanel(currSensorA)) {
11749                levelXing.setSensorAName(null);
11750            }
11751        } else if (xingSensorA != null && levelXing.getConnectA() != null) {
11752            setLevelXingSensor(aSensor, currSensorA, xingSensorA, levelXing.getConnectA(), levelXingView.getCoordsA(), "A");
11753        }
11754
11755        if (bSensor == null) {
11756            if (currSensorB != null && removeSensorFromPanel(currSensorB)) {
11757                levelXing.setSensorBName(null);
11758            }
11759        } else if (xingSensorB != null && levelXing.getConnectB() != null) {
11760            setLevelXingSensor(bSensor, currSensorB, xingSensorB, levelXing.getConnectB(), levelXingView.getCoordsB(), "B");
11761        }
11762
11763        if (cSensor == null) {
11764            if (currSensorC != null && removeSensorFromPanel(currSensorC)) {
11765                levelXing.setSensorCName(null);
11766            }
11767        } else if (xingSensorC != null && levelXing.getConnectC() != null) {
11768            setLevelXingSensor(cSensor, currSensorC, xingSensorC, levelXing.getConnectC(), levelXingView.getCoordsC(), "C");
11769        }
11770
11771        if (dSensor == null) {
11772            if (currSensorD != null && removeSensorFromPanel(currSensorD)) {
11773                levelXing.setSensorDName(null);
11774            }
11775        } else if (xingSensorD != null && levelXing.getConnectD() != null) {
11776            setLevelXingSensor(dSensor, currSensorD, xingSensorD, levelXing.getConnectD(), levelXingView.getCoordsD(), "D");
11777        }
11778
11779        //setup logic if requested
11780        //finish up
11781        setSensorsAtLevelXingOpenFlag = false;
11782        setSensorsAtLevelXingFrame.setVisible(false);
11783        if (needRedraw) {
11784            layoutEditor.redrawPanel();
11785            needRedraw = false;
11786            layoutEditor.setDirty();
11787        }
11788    }
11789
11790    /**
11791     * Attached a sensor to a level crossing block boundary.
11792     *
11793     * @since 4.11.2
11794     * @param newSensor  The sensor that is being added.
11795     * @param currSensor The sensor that might already be there, otherwise null.
11796     * @param beanDetail The BeanDetails object that contains the supporting
11797     *                   data.
11798     * @param connect    The track segment that is attached to this point
11799     * @param coords     The track componennt coordinates
11800     * @param position   Which of the four points is being changed
11801     */
11802    void setLevelXingSensor(Sensor newSensor, Sensor currSensor, BeanDetails<? extends NamedBean> beanDetail,
11803            LayoutTrack connect, Point2D coords, String position) {
11804        if (currSensor == null) {
11805            if (!isSensorAssignedAnywhere(newSensor)) {
11806                log.trace("Add sensor '{}'", newSensor.getDisplayName());  // NOI18N
11807                switch (position) {
11808                    case "A":  // NOI18N
11809                        levelXing.setSensorAName(beanDetail.getText());
11810                        break;
11811                    case "B":  // NOI18N
11812                        levelXing.setSensorBName(beanDetail.getText());
11813                        break;
11814                    case "C":  // NOI18N
11815                        levelXing.setSensorCName(beanDetail.getText());
11816                        break;
11817                    case "D":  // NOI18N
11818                        levelXing.setSensorDName(beanDetail.getText());
11819                        break;
11820                    default:
11821                        break;
11822                }
11823                if (beanDetail.addToPanel()) {
11824                    log.trace("Add icon for sensor '{}'", newSensor.getDisplayName());  // NOI18N
11825                    placingBlock(getSensorIcon(beanDetail.getText()),
11826                            beanDetail.isRightSelected(), 0.0, connect, coords);
11827                    needRedraw = true;
11828                }
11829            } else {
11830                sensorAssignedElseWhere(newSensor);
11831            }
11832        } else if (currSensor == newSensor) {
11833            if (beanDetail.addToPanel()) {
11834                if (!isSensorOnPanel(newSensor)) {
11835                    log.trace("Add icon for existing sensor '{}'", newSensor.getDisplayName());  // NOI18N
11836                    placingBlock(getSensorIcon(beanDetail.getText()),
11837                            beanDetail.isRightSelected(), 0.0, connect, coords);
11838                    needRedraw = true;
11839                }
11840            }
11841        } else {
11842            if (!isSensorAssignedAnywhere(newSensor)) {
11843                if (removeSensorFromPanel(currSensor)) {
11844                    log.trace("Replace sensor '{}' with sensor '{}'", // NOI18N
11845                            currSensor.getDisplayName(), newSensor.getDisplayName());
11846                    switch (position) {
11847                        case "A":  // NOI18N
11848                            levelXing.setSensorAName(beanDetail.getText());
11849                            break;
11850                        case "B":  // NOI18N
11851                            levelXing.setSensorBName(beanDetail.getText());
11852                            break;
11853                        case "C":  // NOI18N
11854                            levelXing.setSensorCName(beanDetail.getText());
11855                            break;
11856                        case "D":  // NOI18N
11857                            levelXing.setSensorDName(beanDetail.getText());
11858                            break;
11859                        default:
11860                            break;
11861                    }
11862                    if (beanDetail.addToPanel()) {
11863                        log.trace("Add icon for replacement sensor '{}'", // NOI18N
11864                                newSensor.getDisplayName());
11865                        placingBlock(getSensorIcon(beanDetail.getText()),
11866                                beanDetail.isRightSelected(), 0.0, connect, coords);
11867                        needRedraw = true;
11868                    }
11869                }
11870            } else {
11871                sensorAssignedElseWhere(newSensor);
11872            }
11873        }
11874    }
11875
11876    private boolean getSimpleBlockInformation() {
11877        //might have to do something to trick it with an end bumper
11878        if (!setSignalMastsAtBlockBoundaryFromMenuFlag) {
11879            block1 = getBlockFromEntry(block1IDComboBox);
11880            if (block1 == null) {
11881                return false;
11882            }
11883            block2 = getBlockFromEntry(block2IDComboBox);
11884            boundary = null;
11885            //if block2 is undefined or same as block 1
11886            if (block2 == null || (block1 == block2)) {
11887                //find the 1st positionablePoint that's connect1'ed to block1
11888                for (PositionablePoint p : layoutEditor.getPositionablePoints()) {
11889                    if (p.getType() == PositionablePoint.PointType.END_BUMPER) {
11890                        if (p.getConnect1() != null && p.getConnect1().getLayoutBlock() == block1) {
11891                            boundary = p;
11892                            break;
11893                        }
11894                    }
11895                }
11896            }
11897
11898            //now we try to find an anchor that connected to blocks 1 and 2
11899            //(if this fails boundary will still be set to the pp set if
11900            //block2 was null or equal to block1 above.)
11901            for (PositionablePoint p : layoutEditor.getPositionablePoints()) {
11902                if (p.getType() != PositionablePoint.PointType.END_BUMPER) {
11903                    LayoutBlock bA = null;
11904                    LayoutBlock bB = null;
11905                    if (p.getConnect1() != null) {
11906                        bA = p.getConnect1().getLayoutBlock();
11907                    }
11908                    if (p.getConnect2() != null) {
11909                        bB = p.getConnect2().getLayoutBlock();
11910                    }
11911                    if ((bA != null) && (bB != null) && (bA != bB)) {
11912                        if (((bA == block1) && (bB == block2))
11913                                || ((bA == block2) && (bB == block1))) {
11914                            boundary = p;
11915                            break;
11916                        }
11917                    }
11918                }
11919            }
11920            //if all that failed...
11921            if (boundary == null) {
11922                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
11923                        Bundle.getMessage("SignalsError7"),
11924                        Bundle.getMessage("ErrorTitle"),
11925                        JmriJOptionPane.ERROR_MESSAGE);
11926                return false;
11927            }
11928        }
11929        return true;
11930    }
11931
11932    /*==================*\
11933    |* setSensorsAtSlip *|
11934    \*==================*/
11935    //operational variables for Set Sensors at Slip tool
11936    private JmriJFrame setSensorsAtSlipFrame = null;
11937    private boolean setSensorsAtSlipOpenFlag = false;
11938    private boolean setSensorsAtSlipFromMenuFlag = false;
11939
11940    private JButton getSavedSlipSensors = null;
11941    private JButton setSlipSensorsDone = null;
11942    private JButton setSlipSensorsCancel = null;
11943    private JButton changeSensorSlipIcon = null;
11944    JFrame sensorSlipFrame = null;
11945
11946    private final NamedBeanComboBox<Block> slipSensorsBlockAComboBox
11947            = new NamedBeanComboBox<>(
11948                    InstanceManager.getDefault(BlockManager.class),
11949                    null, DisplayOptions.DISPLAYNAME);
11950    private final NamedBeanComboBox<Block> slipSensorsBlockBComboBox
11951            = new NamedBeanComboBox<>(
11952                    InstanceManager.getDefault(BlockManager.class),
11953                    null, DisplayOptions.DISPLAYNAME);
11954    private final NamedBeanComboBox<Block> slipSensorsBlockCComboBox
11955            = new NamedBeanComboBox<>(
11956                    InstanceManager.getDefault(BlockManager.class),
11957                    null, DisplayOptions.DISPLAYNAME);
11958    private final NamedBeanComboBox<Block> slipSensorsBlockDComboBox
11959            = new NamedBeanComboBox<>(
11960                    InstanceManager.getDefault(BlockManager.class),
11961                    null, DisplayOptions.DISPLAYNAME);
11962
11963    private String[] slipSensorBlocks = new String[4];
11964
11965    BeanDetails<Sensor> slipSensorA;
11966    BeanDetails<Sensor> slipSensorB;
11967    BeanDetails<Sensor> slipSensorC;
11968    BeanDetails<Sensor> slipSensorD;
11969
11970    JPanel sensorSlipPanel = new JPanel(new FlowLayout());
11971
11972    //display dialog for Set Signals at Level Crossing tool
11973    public void setSensorsAtSlipFromMenu(@Nonnull LayoutSlip slip,
11974            @Nonnull String[] blocks,
11975            @Nonnull MultiIconEditor theEditor,
11976            @Nonnull JFrame theFrame) {
11977        layoutSlip = slip;
11978        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
11979        slipSensorsBlockAComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockName()));
11980        slipSensorsBlockBComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockBName()));
11981        slipSensorsBlockCComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockCName()));
11982        slipSensorsBlockDComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockDName()));
11983        for (int i = 0; i < blocks.length; i++) {
11984            slipSensorBlocks[i] = blocks[i];
11985        }
11986        setSensorsAtSlipFromMenuFlag = true;
11987        setSensorsAtSlip(theEditor, theFrame);
11988        setSensorsAtSlipFromMenuFlag = false;
11989    }
11990
11991    //TODO: Add to Tools menu?
11992    public void setSensorsAtSlip(@Nonnull MultiIconEditor theEditor,
11993            @Nonnull JFrame theFrame) {
11994        sensorIconEditor = theEditor;
11995        sensorSlipFrame = theFrame;
11996
11997        //Initialize if needed
11998        if (setSensorsAtSlipFrame == null) {
11999            setSensorsAtSlipOpenFlag = false;
12000
12001            slipSensorA = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
12002            slipSensorB = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
12003            slipSensorC = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
12004            slipSensorD = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
12005
12006            slipSensorA.getDetailsPanel().setBackground(new Color(255, 255, 200));
12007            slipSensorB.getDetailsPanel().setBackground(new Color(200, 255, 255));
12008            slipSensorC.getDetailsPanel().setBackground(new Color(200, 200, 255));
12009            slipSensorD.getDetailsPanel().setBackground(new Color(255, 200, 200));
12010
12011            setSensorsAtSlipFrame = new JmriJFrame(Bundle.getMessage("SensorsAtSlip"), false, true);
12012            oneFrameToRuleThemAll(setSensorsAtSlipFrame);
12013            setSensorsAtSlipFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
12014//         setSensorsAtSlipFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtLevelSlip", true);
12015            setSensorsAtSlipFrame.setLocation(70, 30);
12016            Container theContentPane = setSensorsAtSlipFrame.getContentPane();
12017            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
12018
12019            JPanel panel11A = new JPanel(new FlowLayout());
12020            slipSignalBlockANameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12021                    Bundle.getMessage("BeanNameBlock") + " A "
12022                    + Bundle.getMessage("Name")));
12023            panel11A.add(slipSignalBlockANameLabel);
12024            panel11A.add(slipSensorsBlockAComboBox);
12025            slipSensorsBlockAComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12026            theContentPane.add(panel11A);
12027
12028            JPanel panel11B = new JPanel(new FlowLayout());
12029            slipSignalBlockBNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12030                    Bundle.getMessage("BeanNameBlock") + " B "
12031                    + Bundle.getMessage("Name")));
12032            panel11B.add(slipSignalBlockBNameLabel);
12033            panel11B.add(slipSensorsBlockBComboBox);
12034            slipSensorsBlockBComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12035            theContentPane.add(panel11B);
12036
12037            JPanel panel11C = new JPanel(new FlowLayout());
12038            slipSignalBlockCNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12039                    Bundle.getMessage("BeanNameBlock") + " C "
12040                    + Bundle.getMessage("Name")));
12041            panel11C.add(slipSignalBlockCNameLabel);
12042            panel11C.add(slipSensorsBlockCComboBox);
12043            slipSensorsBlockCComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12044            theContentPane.add(panel11C);
12045
12046            JPanel panel11D = new JPanel(new FlowLayout());
12047            slipSignalBlockDNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12048                    Bundle.getMessage("BeanNameBlock") + " D "
12049                    + Bundle.getMessage("Name")));
12050            panel11D.add(slipSignalBlockDNameLabel);
12051            panel11D.add(slipSensorsBlockDComboBox);
12052            slipSensorsBlockDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12053            theContentPane.add(panel11D);
12054
12055            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12056
12057            JPanel panel2 = new JPanel(new FlowLayout());
12058            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSensor"));
12059            panel2.add(shTitle);
12060            panel2.add(new JLabel("   "));
12061            panel2.add(getSavedSlipSensors = new JButton(Bundle.getMessage("GetSaved")));
12062            getSavedSlipSensors.addActionListener(this::slipSensorsGetSaved);
12063            getSavedSlipSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
12064            theContentPane.add(panel2);
12065
12066            sensorSlipPanel.setLayout(new GridLayout(0, 2));
12067            theContentPane.add(sensorSlipPanel);
12068            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12069
12070            JPanel panel6 = new JPanel(new FlowLayout());
12071            panel6.add(changeSensorSlipIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
12072            changeSensorSlipIcon.addActionListener((ActionEvent e) -> sensorSlipFrame.setVisible(true));
12073            changeSensorSlipIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
12074
12075            panel6.add(new JLabel("   "));
12076            panel6.add(setSlipSensorsDone = new JButton(Bundle.getMessage("ButtonDone")));
12077            setSlipSensorsDone.addActionListener(this::setSlipSensorsDonePressed);
12078            setSlipSensorsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
12079            panel6.add(setSlipSensorsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
12080            setSlipSensorsCancel.addActionListener(this::setSlipSensorsCancelPressed);
12081            setSlipSensorsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
12082            theContentPane.add(panel6);
12083            setSensorsAtSlipFrame.addWindowListener(new WindowAdapter() {
12084                @Override
12085                public void windowClosing(WindowEvent e) {
12086                    setSlipSensorsCancelPressed(null);
12087                }
12088            });
12089        }   //if (setSensorsAtSlipFrame == null)
12090
12091        sensorSlipPanel.removeAll();
12092
12093        slipSensorsBlockAComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12094        slipSensorsBlockBComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12095        slipSensorsBlockCComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12096        slipSensorsBlockDComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12097
12098        if (setSensorsAtSlipFromMenuFlag) {
12099            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
12100                    Bundle.getMessage("BeanNameBlock") + " A "
12101                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockName());
12102            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
12103                    Bundle.getMessage("BeanNameBlock") + " B "
12104                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockBName());
12105            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
12106                    Bundle.getMessage("BeanNameBlock") + " C "
12107                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockCName());
12108            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
12109                    Bundle.getMessage("BeanNameBlock") + " D "
12110                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockDName());
12111            slipSensorsGetSaved(null);
12112        } else {
12113            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
12114                    Bundle.getMessage("BeanNameBlock") + " A "
12115                    + Bundle.getMessage("Name")));
12116            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
12117                    Bundle.getMessage("BeanNameBlock") + " B "
12118                    + Bundle.getMessage("Name")));
12119            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
12120                    Bundle.getMessage("BeanNameBlock") + " C "
12121                    + Bundle.getMessage("Name")));
12122            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
12123                    Bundle.getMessage("BeanNameBlock") + " D "
12124                    + Bundle.getMessage("Name")));
12125        }
12126
12127        if (setSensorsAtSlipOpenFlag) {
12128            slipSensorsGetSaved(null);
12129        } else {
12130            setSensorsAtSlipFrame.setPreferredSize(null);
12131            setSensorsAtSlipFrame.setVisible(true);
12132            setSensorsAtSlipOpenFlag = true;
12133        }
12134        setSensorsAtSlipFrame.setPreferredSize(null);
12135        setSensorsAtSlipFrame.pack();
12136    }
12137
12138    private void slipSensorsGetSaved(ActionEvent a) {
12139        if (!getSlipSensorInformation()) {
12140            return;
12141        }
12142
12143        slipSensorBlocks = layoutSlip.getBlockBoundaries();
12144
12145        slipSensorA.setTextField(layoutSlip.getSensorAName());
12146        slipSensorB.setTextField(layoutSlip.getSensorBName());
12147        slipSensorC.setTextField(layoutSlip.getSensorCName());
12148        slipSensorD.setTextField(layoutSlip.getSensorDName());
12149
12150        sensorSlipPanel.remove(slipSensorA.getDetailsPanel());
12151        sensorSlipPanel.remove(slipSensorB.getDetailsPanel());
12152        sensorSlipPanel.remove(slipSensorC.getDetailsPanel());
12153        sensorSlipPanel.remove(slipSensorD.getDetailsPanel());
12154
12155        slipSensorA.setBoundaryLabel(slipSensorBlocks[0]);
12156        slipSensorB.setBoundaryLabel(slipSensorBlocks[1]);
12157        slipSensorC.setBoundaryLabel(slipSensorBlocks[2]);
12158        slipSensorD.setBoundaryLabel(slipSensorBlocks[3]);
12159
12160        boolean boundaryFlag = false;
12161        if (slipSensorBlocks[0] != null) {
12162            sensorSlipPanel.add(slipSensorA.getDetailsPanel());
12163            boundaryFlag = true;
12164        }
12165        if (slipSensorBlocks[1] != null) {
12166            sensorSlipPanel.add(slipSensorB.getDetailsPanel());
12167            boundaryFlag = true;
12168        }
12169        if (slipSensorBlocks[2] != null) {
12170            sensorSlipPanel.add(slipSensorC.getDetailsPanel());
12171            boundaryFlag = true;
12172        }
12173        if (slipSensorBlocks[3] != null) {
12174            sensorSlipPanel.add(slipSensorD.getDetailsPanel());
12175            boundaryFlag = true;
12176        }
12177        if (!boundaryFlag) {
12178            JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame, Bundle.getMessage("NoBoundarySlipSensor"));
12179        }
12180        setSensorsAtSlipFrame.setPreferredSize(null);
12181        setSensorsAtSlipFrame.pack();
12182    }
12183
12184    private boolean getSlipSensorInformation() {
12185        if (!setSensorsAtSlipFromMenuFlag) {
12186            layoutSlip = null;
12187            List<LayoutSlip> layoutSlips = layoutEditor.getLayoutSlips();
12188            if (layoutSlips.size() <= 0) {
12189                JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame,
12190                        Bundle.getMessage("SignalsError15"),
12191                        Bundle.getMessage("ErrorTitle"),
12192                        JmriJOptionPane.ERROR_MESSAGE);
12193                return false;
12194            } else if (layoutSlips.size() == 1) {
12195                layoutSlip = layoutSlips.get(0);
12196            } else {
12197                LayoutBlock slipSensorBlockA = null;
12198                slipSensorBlockA = getBlockFromEntry(slipSensorsBlockAComboBox);
12199                if (slipSensorBlockA == null) {
12200                    return false;
12201                }
12202
12203                int foundCount = 0;
12204                for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
12205                    LayoutBlock xA = null;
12206                    LayoutBlock xB = null;
12207                    LayoutBlock xC = null;
12208                    LayoutBlock xD = null;
12209
12210                    LayoutBlock xAC = x.getLayoutBlock();
12211                    if (x.getConnectA() != null) {
12212                        xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
12213                    }
12214                    if (x.getConnectB() != null) {
12215                        xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
12216                    }
12217                    if (x.getConnectC() != null) {
12218                        xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
12219                    }
12220                    if (x.getConnectD() != null) {
12221                        xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
12222                    }
12223                    if (((xA != null) && (xC != null) && ((xA == slipSensorBlockA)
12224                            || (xC == slipSensorBlockA)))
12225                            || ((xB != null) && (xD != null) && (((xB == slipSensorBlockA))
12226                            || ((xD == slipSensorBlockA))))) {
12227                        layoutSlip = x;
12228                        foundCount++;
12229                    } else if ((xAC != null) && (xAC == slipSensorBlockA)) {
12230                        layoutSlip = x;
12231                        foundCount++;
12232                    }
12233                }
12234                if (foundCount == 0) {
12235                    //try one block test
12236                    for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
12237                        if (slipSensorBlockA == x.getLayoutBlock()) {
12238                            layoutSlip = x;
12239                            foundCount++;
12240                        }
12241                    }
12242                }
12243                if (foundCount > 1) {
12244                    JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame,
12245                            Bundle.getMessage("SignalsError16",
12246                                    new Object[]{" " + foundCount + " "}),
12247                            Bundle.getMessage("ErrorTitle"),
12248                            JmriJOptionPane.ERROR_MESSAGE);
12249                    return false;
12250                }
12251                if (layoutSlip == null) {
12252                    JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame,
12253                            Bundle.getMessage("SignalsError17"),
12254                            Bundle.getMessage("ErrorTitle"),
12255                            JmriJOptionPane.ERROR_MESSAGE);
12256                    return false;
12257                }
12258            }
12259        }
12260        return true;
12261    }
12262
12263    private void setSlipSensorsCancelPressed(ActionEvent a) {
12264        setSensorsAtSlipOpenFlag = false;
12265        setSensorsAtSlipFrame.setVisible(false);
12266    }
12267
12268    private void setSlipSensorsDonePressed(ActionEvent a) {
12269        log.trace("setSlipSensorsDonePressed");
12270        if (!getSlipSensorInformation()) {
12271            return;
12272        }
12273
12274        Sensor sensorA = getSensorFromEntry(slipSensorA.getText(), false, setSensorsAtSlipFrame);
12275        Sensor sensorB = getSensorFromEntry(slipSensorB.getText(), false, setSensorsAtSlipFrame);
12276        Sensor sensorC = getSensorFromEntry(slipSensorC.getText(), false, setSensorsAtSlipFrame);
12277        Sensor sensorD = getSensorFromEntry(slipSensorD.getText(), false, setSensorsAtSlipFrame);
12278
12279        Sensor currSensorA = layoutSlip.getSensorA();
12280        Sensor currSensorB = layoutSlip.getSensorB();
12281        Sensor currSensorC = layoutSlip.getSensorC();
12282        Sensor currSensorD = layoutSlip.getSensorD();
12283
12284        if (log.isTraceEnabled()) {
12285            log.trace("current sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
12286                    (currSensorA == null) ? "- none- " : currSensorA.getDisplayName(), // NOI18N
12287                    (currSensorB == null) ? "- none- " : currSensorB.getDisplayName(), // NOI18N
12288                    (currSensorC == null) ? "- none- " : currSensorC.getDisplayName(), // NOI18N
12289                    (currSensorD == null) ? "- none- " : currSensorD.getDisplayName());  // NOI18N
12290            log.trace("new sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
12291                    (sensorA == null) ? "- none- " : sensorA.getDisplayName(), // NOI18N
12292                    (sensorB == null) ? "- none- " : sensorB.getDisplayName(), // NOI18N
12293                    (sensorC == null) ? "- none- " : sensorC.getDisplayName(), // NOI18N
12294                    (sensorD == null) ? "- none- " : sensorD.getDisplayName());  // NOI18N
12295        }
12296
12297        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
12298
12299        //place/remove sensors as requested
12300        if (sensorA == null) {
12301            if (currSensorA != null && removeSensorFromPanel(currSensorA)) {
12302                layoutSlip.setSensorA(null);
12303            }
12304        } else if (slipSensorA != null && layoutSlip.getConnectA() != null) {
12305            setTurnoutSensor(layoutSlip, sensorA, currSensorA, slipSensorA, layoutSlip.getConnectA(), layoutSlipView.getCoordsA(), "A");
12306        }
12307
12308        if (sensorB == null) {
12309            if (currSensorB != null && removeSensorFromPanel(currSensorB)) {
12310                layoutSlip.setSensorB(null);
12311            }
12312        } else if (slipSensorB != null && layoutSlip.getConnectB() != null) {
12313            setTurnoutSensor(layoutSlip, sensorB, currSensorB, slipSensorB, layoutSlip.getConnectB(), layoutSlipView.getCoordsB(), "B");
12314        }
12315
12316        if (sensorC == null) {
12317            if (currSensorC != null && removeSensorFromPanel(currSensorC)) {
12318                layoutSlip.setSensorC(null);
12319            }
12320        } else if (slipSensorC != null && layoutSlip.getConnectC() != null) {
12321            setTurnoutSensor(layoutSlip, sensorC, currSensorC, slipSensorC, layoutSlip.getConnectC(), layoutSlipView.getCoordsC(), "C");
12322        }
12323
12324        if (sensorD == null) {
12325            if (currSensorD != null && removeSensorFromPanel(currSensorD)) {
12326                layoutSlip.setSensorD(null);
12327            }
12328        } else if (slipSensorD != null && layoutSlip.getConnectD() != null) {
12329            setTurnoutSensor(layoutSlip, sensorD, currSensorD, slipSensorD, layoutSlip.getConnectD(), layoutSlipView.getCoordsD(), "D");
12330        }
12331
12332        //setup logic if requested
12333        //finish up
12334        setSensorsAtSlipOpenFlag = false;
12335        setSensorsAtSlipFrame.setVisible(false);
12336        if (needRedraw) {
12337            layoutEditor.redrawPanel();
12338            needRedraw = false;
12339            layoutEditor.setDirty();
12340
12341        }
12342    }
12343
12344    static class BeanDetails<B extends NamedBean> {
12345
12346        private final String bundleName;
12347        private final String beanString;
12348        private final JLabel textLabel;
12349
12350        private final String boundaryLabelText = Bundle.getMessage("BoundaryOf");
12351        private final JLabel boundaryLabel = new JLabel(boundaryLabelText);
12352
12353        private final Manager<B> manager;
12354
12355        private final JPanel detailsPanel = new JPanel(new FlowLayout());
12356        private final JRadioButton addBeanCheck = new JRadioButton(Bundle.getMessage("DoNotPlace"));
12357        private final JRadioButton left = new JRadioButton(Bundle.getMessage("LeftHandSide"));
12358        private final JRadioButton right = new JRadioButton(Bundle.getMessage("RightHandSide"));
12359        private final ButtonGroup buttonGroup = new ButtonGroup();
12360        private final NamedBeanComboBox<B> beanCombo;
12361
12362        private final JLabel boundaryBlocks = new JLabel();
12363
12364        private final Border blackline = BorderFactory.createLineBorder(Color.black);
12365
12366        BeanDetails(@Nonnull String beanType, @Nonnull Manager<B> manager) {
12367            beanCombo = new NamedBeanComboBox<>(manager);
12368            beanCombo.setAllowNull(true);
12369            JComboBoxUtil.setupComboBoxMaxRows(beanCombo);
12370            //I18N translate from type (Sensor) to BeanNameSensor
12371            //to use NamedBeanBundle property
12372            if ("Sensor".equals(beanType)) {
12373                bundleName = "BeanNameSensor";
12374            } else if ("SignalMast".equals(beanType)) {
12375                bundleName = "BeanNameSignalMast";
12376            } else {
12377                log.error("Unexpected value for BeanDetails: '{}'", beanType);
12378                bundleName = beanType;
12379            }
12380            beanString = Bundle.getMessage(bundleName);
12381            textLabel = new JLabel(beanString);
12382            this.manager = manager;
12383            //this.beanType = beanType;
12384
12385            buttonGroup.add(addBeanCheck);
12386            buttonGroup.add(left);
12387            buttonGroup.add(right);
12388            addBeanCheck.setSelected(true);
12389
12390            boundaryBlocks.setAlignmentX(Component.CENTER_ALIGNMENT);
12391            boundaryBlocks.setOpaque(false);
12392            detailsPanel.setLayout(new BorderLayout());
12393            detailsPanel.setBorder(BorderFactory.createTitledBorder(blackline, Bundle.getMessage("BlockBoundary")));
12394            boundaryLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
12395
12396            JPanel boundaryDetails = new JPanel(new FlowLayout());
12397            boundaryDetails.setOpaque(false);
12398            boundaryDetails.setLayout(new BoxLayout(boundaryDetails, BoxLayout.Y_AXIS));
12399            boundaryDetails.add(boundaryLabel);
12400            boundaryDetails.add(boundaryBlocks);
12401
12402            detailsPanel.add(boundaryDetails, BorderLayout.PAGE_START);
12403            detailsPanel.add(addIconPanel(), BorderLayout.CENTER);
12404            detailsPanel.add(positionLeftRight(), BorderLayout.PAGE_END);
12405        }
12406
12407        protected void setTextField(String value) {
12408            beanCombo.setSelectedItem(manager.getNamedBean(value));
12409        }
12410
12411        protected String getText() {
12412            return beanCombo.getSelectedItemDisplayName();
12413        }
12414
12415        protected B getBean() {
12416            return beanCombo.getSelectedItem();
12417        }
12418
12419        protected JPanel getDetailsPanel() {
12420            return detailsPanel;
12421        }
12422
12423        protected boolean addToPanel() {
12424            return !addBeanCheck.isSelected();
12425        }
12426
12427        protected boolean isRightSelected() {
12428            return right.isSelected();
12429        }
12430
12431        protected void setBoundaryTitle(String text) {
12432            detailsPanel.setBorder(BorderFactory.createTitledBorder(blackline, text));
12433        }
12434
12435        protected void setBoundaryLabelText(String text) {
12436            boundaryLabel.setText(text);
12437        }
12438
12439        protected void setBoundaryLabel(String label) {
12440            boundaryBlocks.setText(label);
12441        }
12442
12443        protected NamedBeanComboBox<B> getCombo() {
12444            return beanCombo;
12445        }
12446
12447        protected JPanel positionLeftRight() {
12448            JPanel placementPanel = new JPanel();
12449            placementPanel.setBorder(BorderFactory.createTitledBorder(
12450                    blackline,
12451                    Bundle.getMessage("PlaceItem", new Object[]{beanString})));
12452
12453            placementPanel.setLayout(new BoxLayout(placementPanel, BoxLayout.Y_AXIS));
12454            placementPanel.setOpaque(false);
12455            placementPanel.add(addBeanCheck);
12456            placementPanel.add(left);
12457            placementPanel.add(right);
12458            addBeanCheck.setOpaque(false);
12459            left.setOpaque(false);
12460            right.setOpaque(false);
12461
12462            addBeanCheck.setToolTipText(Bundle.getMessage("PlaceItemToolTip",
12463                    new Object[]{beanString}));
12464
12465            right.setToolTipText(Bundle.getMessage("PlaceRightToolTip",
12466                    new Object[]{beanString}));
12467
12468            left.setToolTipText(Bundle.getMessage("PlaceLeftToolTip",
12469                    new Object[]{beanString}));
12470            return placementPanel;
12471        }
12472
12473        protected JPanel addIconPanel() {
12474            JPanel addBeanPanel = new JPanel(new FlowLayout());
12475            addBeanPanel.setOpaque(false);
12476            addBeanPanel.add(textLabel);
12477            textLabel.setOpaque(false);
12478            addBeanPanel.add(beanCombo);
12479            return addBeanPanel;
12480        }
12481    }
12482
12483    /*==================*\
12484    |* setSignalsAtSlip *|
12485    \*==================*/
12486//operational variables for Set Signals at slip tool
12487    private JmriJFrame setSignalsAtSlipFrame = null;
12488    private boolean setSignalsAtSlipOpenFlag = false;
12489    private boolean setSignalsAtSlipFromMenuFlag = false;
12490
12491    private final JComboBox<String> slipNameComboBox = new JComboBox<>();
12492
12493    private final NamedBeanComboBox<SignalHead> a1SlipSignalHeadComboBox
12494            = new NamedBeanComboBox<>(
12495                    InstanceManager.getDefault(SignalHeadManager.class),
12496                    null, DisplayOptions.DISPLAYNAME);
12497    private final NamedBeanComboBox<SignalHead> a2SlipSignalHeadComboBox
12498            = new NamedBeanComboBox<>(
12499                    InstanceManager.getDefault(SignalHeadManager.class),
12500                    null, DisplayOptions.DISPLAYNAME);
12501    private final NamedBeanComboBox<SignalHead> b1SlipSignalHeadComboBox
12502            = new NamedBeanComboBox<>(
12503                    InstanceManager.getDefault(SignalHeadManager.class),
12504                    null, DisplayOptions.DISPLAYNAME);
12505    private final NamedBeanComboBox<SignalHead> b2SlipSignalHeadComboBox
12506            = new NamedBeanComboBox<>(
12507                    InstanceManager.getDefault(SignalHeadManager.class),
12508                    null, DisplayOptions.DISPLAYNAME);
12509    private final NamedBeanComboBox<SignalHead> c1SlipSignalHeadComboBox
12510            = new NamedBeanComboBox<>(
12511                    InstanceManager.getDefault(SignalHeadManager.class),
12512                    null, DisplayOptions.DISPLAYNAME);
12513    private final NamedBeanComboBox<SignalHead> c2SlipSignalHeadComboBox
12514            = new NamedBeanComboBox<>(
12515                    InstanceManager.getDefault(SignalHeadManager.class),
12516                    null, DisplayOptions.DISPLAYNAME);
12517    private final NamedBeanComboBox<SignalHead> d1SlipSignalHeadComboBox
12518            = new NamedBeanComboBox<>(
12519                    InstanceManager.getDefault(SignalHeadManager.class),
12520                    null, DisplayOptions.DISPLAYNAME);
12521    private final NamedBeanComboBox<SignalHead> d2SlipSignalHeadComboBox
12522            = new NamedBeanComboBox<>(
12523                    InstanceManager.getDefault(SignalHeadManager.class),
12524                    null, DisplayOptions.DISPLAYNAME);
12525
12526    private final JCheckBox setA1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12527    private final JCheckBox setupA1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12528    private final JCheckBox setA2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12529    private final JCheckBox setupA2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12530    private final JCheckBox setB1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12531    private final JCheckBox setupB1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12532    private final JCheckBox setB2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12533    private final JCheckBox setupB2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12534    private final JCheckBox setC1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12535    private final JCheckBox setupC1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12536    private final JCheckBox setC2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12537    private final JCheckBox setupC2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12538    private final JCheckBox setD1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12539    private final JCheckBox setupD1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12540    private final JCheckBox setD2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12541    private final JCheckBox setupD2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12542
12543    private JButton setSlipSignalsDone = null;
12544    private JButton setSlipSignalsCancel = null;
12545    private LayoutSlip layoutSlip = null;
12546
12547    private SignalHead a1SlipHead = null;
12548    private SignalHead a2SlipHead = null;
12549    private SignalHead b1SlipHead = null;
12550    private SignalHead b2SlipHead = null;
12551    private SignalHead c1SlipHead = null;
12552    private SignalHead c2SlipHead = null;
12553    private SignalHead d1SlipHead = null;
12554    private SignalHead d2SlipHead = null;
12555
12556    private JPanel dblSlipC2SigPanel;
12557    private JPanel dblSlipB2SigPanel;
12558
12559    public void setSignalsAtSlipFromMenu(@Nonnull LayoutSlip ls,
12560            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
12561        layoutSlip = ls;
12562        a1SlipSignalHeadComboBox.setSelectedItem(null);
12563        a2SlipSignalHeadComboBox.setSelectedItem(null);
12564        b1SlipSignalHeadComboBox.setSelectedItem(null);
12565        b2SlipSignalHeadComboBox.setSelectedItem(null);
12566        c1SlipSignalHeadComboBox.setSelectedItem(null);
12567        c2SlipSignalHeadComboBox.setSelectedItem(null);
12568        d1SlipSignalHeadComboBox.setSelectedItem(null);
12569        d2SlipSignalHeadComboBox.setSelectedItem(null);
12570
12571        setSignalsAtSlipFromMenuFlag = true;
12572        setSignalsAtSlip(theEditor, theFrame);
12573        setSignalsAtSlipFromMenuFlag = false;
12574    }
12575
12576    public void setSignalsAtSlip(@Nonnull MultiIconEditor theEditor,
12577            @Nonnull JFrame theFrame) {
12578        signalIconEditor = theEditor;
12579        signalFrame = theFrame;
12580
12581        //Initialize if needed
12582        if (setSignalsAtSlipFrame == null) {
12583            setSignalsAtSlipOpenFlag = false;
12584            setSignalsAtSlipFrame = new JmriJFrame(Bundle.getMessage("SignalsAtSlip"), false, true);
12585            oneFrameToRuleThemAll(setSignalsAtSlipFrame);
12586            setSignalsAtSlipFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
12587            setSignalsAtSlipFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtSlip", true);
12588            setSignalsAtSlipFrame.setLocation(70, 30);
12589            Container theContentPane = setSignalsAtSlipFrame.getContentPane();
12590            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
12591
12592            JPanel panel1 = new JPanel(new FlowLayout());
12593            JLabel turnout1NameLabel = new JLabel(Bundle.getMessage("Slip") + " "
12594                    + Bundle.getMessage("Name"));
12595            panel1.add(turnout1NameLabel);
12596            panel1.add(slipNameComboBox);
12597            for (LayoutSlip slip : layoutEditor.getLayoutSlips()) {
12598                slipNameComboBox.addItem(slip.getDisplayName());
12599            }
12600
12601            slipNameComboBox.insertItemAt("", 0);
12602
12603            if (layoutSlip != null) {
12604                slipNameComboBox.setSelectedItem(layoutSlip.getDisplayName());
12605                getSlipTurnoutSignalsGetSaved(null);
12606            } else {
12607                slipNameComboBox.setSelectedIndex(0);
12608            }
12609            slipNameComboBox.addActionListener((ActionEvent e) -> {
12610                for (LayoutSlip slip : layoutEditor.getLayoutSlips()) {
12611                    if (slip.getDisplayName().equals(slipNameComboBox.getSelectedItem())) {
12612                        //slip1NameField.setText(slip.getDisplayName());
12613                        getSlipTurnoutSignalsGetSaved(e);
12614                        boolean enable = (slip.getSlipType() == LayoutSlip.TurnoutType.DOUBLE_SLIP);
12615                        dblSlipC2SigPanel.setVisible(enable);
12616                        dblSlipB2SigPanel.setVisible(enable);
12617                        setSignalsAtSlipFrame.pack();
12618                        return;
12619                    }
12620                }
12621            });
12622            theContentPane.add(panel1);
12623            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12624
12625            JPanel panel2a = new JPanel(new FlowLayout());
12626            panel2a.add(new JLabel("   "));
12627            panel2a.add(setPlaceAllHeads);
12628            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
12629            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
12630                boolean isSelected = setPlaceAllHeads.isSelected();
12631                //(de)select all checkboxes
12632                setA1SlipHead.setSelected(isSelected);
12633                setA2SlipHead.setSelected(isSelected);
12634                setB1SlipHead.setSelected(isSelected);
12635                setB2SlipHead.setSelected(isSelected);
12636                setC1SlipHead.setSelected(isSelected);
12637                setC2SlipHead.setSelected(isSelected);
12638                setD1SlipHead.setSelected(isSelected);
12639                setD2SlipHead.setSelected(isSelected);
12640            });
12641            panel2a.add(new JLabel("  "));
12642            panel2a.add(setupAllLogic);
12643            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
12644            setupAllLogic.addActionListener((ActionEvent e) -> {
12645                boolean isSelected = setupAllLogic.isSelected();
12646                //(de)select all checkboxes
12647                setupA1SlipLogic.setSelected(isSelected);
12648                setupA2SlipLogic.setSelected(isSelected);
12649                setupB1SlipLogic.setSelected(isSelected);
12650                setupB2SlipLogic.setSelected(isSelected);
12651                setupC1SlipLogic.setSelected(isSelected);
12652                setupC2SlipLogic.setSelected(isSelected);
12653                setupD1SlipLogic.setSelected(isSelected);
12654                setupD2SlipLogic.setSelected(isSelected);
12655            });
12656            theContentPane.add(panel2a);
12657            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12658
12659            //Signal heads located at turnout 1
12660            JPanel panel21x = new JPanel(new FlowLayout());
12661            panel21x.add(new JLabel(Bundle.getMessage("SignalLocated")
12662                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
12663                    + Bundle.getMessage("ContinuingTrack")));
12664            theContentPane.add(panel21x);
12665
12666            JPanel panel21 = new JPanel(new FlowLayout());
12667            panel21.add(new JLabel(Bundle.getMessage("MakeLabel",
12668                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12669                    + Bundle.getMessage("ContinuingTrack"))));
12670            panel21.add(a1SlipSignalHeadComboBox);
12671            theContentPane.add(panel21);
12672            a1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12673
12674            JPanel panel22 = new JPanel(new FlowLayout());
12675            panel22.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
12676            panel22.add(setA1SlipHead);
12677            setA1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12678            panel22.add(new JLabel("  "));
12679            panel22.add(setupA1SlipLogic);
12680            setupA1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12681            theContentPane.add(panel22);
12682
12683            JPanel panel23 = new JPanel(new FlowLayout());
12684            panel23.add(new JLabel(Bundle.getMessage("MakeLabel",
12685                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12686                    + Bundle.getMessage("DivergingTrack"))));
12687            panel23.add(a2SlipSignalHeadComboBox);
12688            theContentPane.add(panel23);
12689            a2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12690
12691            JPanel panel24 = new JPanel(new FlowLayout());
12692            panel24.add(new JLabel("   "));
12693            panel24.add(setA2SlipHead);
12694            setA2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12695            panel24.add(new JLabel("  "));
12696            panel24.add(setupA2SlipLogic);
12697            setupA2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12698            theContentPane.add(panel24);
12699
12700            JPanel panel31x = new JPanel(new FlowLayout());
12701            panel31x.add(new JLabel(Bundle.getMessage("SignalLocated")
12702                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
12703                    + Bundle.getMessage("DivergingTrack")));
12704            theContentPane.add(panel31x);
12705
12706            JPanel panel31 = new JPanel(new FlowLayout());
12707            panel31.add(new JLabel(Bundle.getMessage("MakeLabel",
12708                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12709                    + Bundle.getMessage("ContinuingTrack"))));
12710            panel31.add(b1SlipSignalHeadComboBox);
12711            theContentPane.add(panel31);
12712            b1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12713
12714            JPanel panel32 = new JPanel(new FlowLayout());
12715            panel32.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
12716            panel32.add(setB1SlipHead);
12717            setB1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12718            panel32.add(new JLabel("  "));
12719            panel32.add(setupB1SlipLogic);
12720            setupB1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12721            theContentPane.add(panel32);
12722
12723            dblSlipB2SigPanel = new JPanel(new FlowLayout());
12724            dblSlipB2SigPanel.setLayout(new BoxLayout(dblSlipB2SigPanel, BoxLayout.Y_AXIS));
12725
12726            JPanel panel33 = new JPanel(new FlowLayout());
12727            panel33.add(new JLabel(Bundle.getMessage("MakeLabel",
12728                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12729                    + Bundle.getMessage("DivergingTrack"))));
12730            panel33.add(b2SlipSignalHeadComboBox);
12731            dblSlipB2SigPanel.add(panel33);
12732            b2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12733
12734            JPanel panel34 = new JPanel(new FlowLayout());
12735            panel34.add(new JLabel("   "));
12736            panel34.add(setB2SlipHead);
12737            setB2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12738            panel34.add(new JLabel("  "));
12739            panel34.add(setupB2SlipLogic);
12740            setupB2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12741            dblSlipB2SigPanel.add(panel34);
12742
12743            theContentPane.add(dblSlipB2SigPanel);
12744            dblSlipB2SigPanel.setVisible(false);
12745            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12746            //Signal heads located at turnout 2
12747
12748            JPanel panel41x = new JPanel(new FlowLayout());
12749            panel41x.add(new JLabel(Bundle.getMessage("SignalLocated")
12750                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
12751                    + Bundle.getMessage("ContinuingTrack")));
12752            theContentPane.add(panel41x);
12753
12754            JPanel panel41 = new JPanel(new FlowLayout());
12755            panel41.add(new JLabel(Bundle.getMessage("MakeLabel",
12756                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12757                    + Bundle.getMessage("ContinuingTrack"))));
12758            panel41.add(c1SlipSignalHeadComboBox);
12759            theContentPane.add(panel41);
12760            c1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12761
12762            JPanel panel42 = new JPanel(new FlowLayout());
12763            panel42.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
12764            panel42.add(setC1SlipHead);
12765            setC1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12766            panel42.add(new JLabel("  "));
12767            panel42.add(setupC1SlipLogic);
12768            setupC1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12769            theContentPane.add(panel42);
12770            dblSlipC2SigPanel = new JPanel(new FlowLayout());
12771            dblSlipC2SigPanel.setLayout(new BoxLayout(dblSlipC2SigPanel, BoxLayout.Y_AXIS));
12772
12773            JPanel panel43 = new JPanel(new FlowLayout());
12774            panel43.add(new JLabel(Bundle.getMessage("MakeLabel",
12775                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
12776                    + Bundle.getMessage("DivergingTrack"))));
12777            panel43.add(c2SlipSignalHeadComboBox);
12778            dblSlipC2SigPanel.add(panel43);
12779            c2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12780
12781            JPanel panel44 = new JPanel(new FlowLayout());
12782            panel44.add(new JLabel("   "));
12783            panel44.add(setC2SlipHead);
12784            setC2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12785            panel44.add(new JLabel("  "));
12786            panel44.add(setupC2SlipLogic);
12787            setupC2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12788            dblSlipC2SigPanel.add(panel44);
12789            theContentPane.add(dblSlipC2SigPanel);
12790
12791            JPanel panel51x = new JPanel(new FlowLayout());
12792            panel51x.add(new JLabel(Bundle.getMessage("SignalLocated")
12793                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
12794                    + Bundle.getMessage("DivergingTrack")));
12795            theContentPane.add(panel51x);
12796
12797            JPanel panel51 = new JPanel(new FlowLayout());
12798            panel51.add(new JLabel(Bundle.getMessage("MakeLabel",
12799                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
12800                    + Bundle.getMessage("ContinuingTrack"))));
12801            panel51.add(d1SlipSignalHeadComboBox);
12802            theContentPane.add(panel51);
12803            d1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12804
12805            JPanel panel52 = new JPanel(new FlowLayout());
12806            panel52.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
12807            panel52.add(setD1SlipHead);
12808            setD1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12809            panel52.add(new JLabel("  "));
12810            panel52.add(setupD1SlipLogic);
12811            setupD1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12812            theContentPane.add(panel52);
12813
12814            JPanel panel53 = new JPanel(new FlowLayout());
12815            panel53.add(new JLabel(Bundle.getMessage("MakeLabel",
12816                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
12817                    + Bundle.getMessage("DivergingTrack"))));
12818            panel53.add(d2SlipSignalHeadComboBox);
12819            theContentPane.add(panel53);
12820            d2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12821
12822            JPanel panel54 = new JPanel(new FlowLayout());
12823            panel54.add(new JLabel("   "));
12824            panel54.add(setD2SlipHead);
12825            setD2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12826            panel54.add(new JLabel("  "));
12827            panel54.add(setupD2SlipLogic);
12828            setupD2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12829            theContentPane.add(panel54);
12830            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12831
12832            JPanel panel6 = new JPanel(new FlowLayout());
12833            panel6.add(changeTToTSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
12834            changeTToTSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
12835            changeTToTSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
12836            panel6.add(new JLabel("   "));
12837            panel6.add(setSlipSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
12838            setSlipSignalsDone.addActionListener(this::setSlipSignalsDonePressed);
12839            setSlipSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
12840            panel6.add(setSlipSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
12841            setSlipSignalsCancel.addActionListener(this::setSlipSignalsCancelPressed);
12842            setSlipSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
12843            theContentPane.add(panel6);
12844            setSignalsAtSlipFrame.addWindowListener(new WindowAdapter() {
12845                @Override
12846                public void windowClosing(WindowEvent e) {
12847                    setSlipSignalsCancelPressed(null);
12848                }
12849            });
12850        }
12851        setPlaceAllHeads.setSelected(false);
12852        setupAllLogic.setSelected(false);
12853
12854        boolean enable = (layoutSlip != null
12855                && layoutSlip.getSlipType() == LayoutSlip.TurnoutType.DOUBLE_SLIP);
12856        dblSlipC2SigPanel.setVisible(enable);
12857        dblSlipB2SigPanel.setVisible(enable);
12858
12859        if (setSignalsAtSlipFromMenuFlag) {
12860            getSlipTurnoutSignalsGetSaved(null);
12861        }
12862
12863        if (!setSignalsAtSlipOpenFlag) {
12864            setSignalsAtSlipFrame.setPreferredSize(null);
12865            setSignalsAtSlipFrame.pack();
12866            setSignalsAtSlipOpenFlag = true;
12867        }
12868        setSignalsAtSlipFrame.setVisible(true);
12869    }
12870
12871    private void getSlipTurnoutSignalsGetSaved(ActionEvent a) {
12872        if (!getSlipTurnoutInformation()) {
12873            return;
12874        }
12875        a1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalA1());
12876        a2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalA2());
12877        b1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalB1());
12878        b2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalB2());
12879        c1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalC1());
12880        c2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalC2());
12881        d1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalD1());
12882        d2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalD2());
12883    }
12884
12885    private void setSlipSignalsCancelPressed(ActionEvent a) {
12886        setSignalsAtSlipOpenFlag = false;
12887        setSignalsAtSlipFrame.setVisible(false);
12888    }
12889
12890    private boolean getSlipTurnoutInformation() {
12891        turnout1 = null;
12892        turnout2 = null;
12893        layoutSlip = null;
12894        for (LayoutSlip ls : layoutEditor.getLayoutSlips()) {
12895            if (ls.getDisplayName().equals(slipNameComboBox.getSelectedItem())) {
12896                layoutSlip = ls;
12897                break;
12898            }
12899        }
12900        if (layoutSlip == null) {
12901            return false;
12902        }
12903        String str = layoutSlip.getDisplayName();
12904
12905        turnout1 = layoutSlip.getTurnout();
12906        if (turnout1 == null) {
12907            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12908                    Bundle.getMessage("SignalsError2",
12909                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
12910                    JmriJOptionPane.ERROR_MESSAGE);
12911            return false;
12912        }
12913        turnout2 = layoutSlip.getTurnoutB();
12914        if (turnout2 == null) {
12915            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12916                    Bundle.getMessage("SignalsError2",
12917                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
12918                    JmriJOptionPane.ERROR_MESSAGE);
12919            return false;
12920        }
12921        return true;
12922    }
12923
12924    private void setSlipSignalsDonePressed(ActionEvent a) {
12925        if (!getSlipTurnoutInformation()) {
12926            return;
12927        }
12928        if (!getSlipSignalHeadInformation()) {
12929            return;
12930        }
12931
12932        //place signal icons if requested, and assign signal heads to this turnout
12933        String signalHeadName = a1SlipSignalHeadComboBox.getSelectedItemDisplayName();
12934        if (signalHeadName == null) {
12935            signalHeadName = "";
12936        }
12937        if (setA1SlipHead.isSelected()) {
12938            if (isHeadOnPanel(a1SlipHead)
12939                    && (a1SlipHead != getHeadFromName(layoutSlip.getSignalB1Name()))) {
12940                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12941                        Bundle.getMessage("SignalsError6",
12942                                new Object[]{signalHeadName}),
12943                        Bundle.getMessage("ErrorTitle"),
12944                        JmriJOptionPane.ERROR_MESSAGE);
12945                return;
12946            } else {
12947                removeSignalHeadFromPanel(layoutSlip.getSignalA1Name());
12948                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
12949                    placeA1Slip(signalHeadName);
12950                } else {
12951                    placeB1Slip(signalHeadName);
12952                }
12953                removeAssignment(a1SlipHead);
12954                layoutSlip.setSignalA1Name(signalHeadName);
12955                needRedraw = true;
12956            }
12957        } else {
12958            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a1SlipHead, layoutSlip);
12959            if (assigned == LayoutTurnout.Geometry.NONE) {
12960                if (isHeadOnPanel(a1SlipHead)
12961                        && isHeadAssignedAnywhere(a1SlipHead)) {
12962                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12963                            Bundle.getMessage("SignalsError8",
12964                                    new Object[]{signalHeadName}),
12965                            Bundle.getMessage("ErrorTitle"),
12966                            JmriJOptionPane.ERROR_MESSAGE);
12967                    return;
12968                } else {
12969                    removeSignalHeadFromPanel(layoutSlip.getSignalB1Name());
12970                    removeAssignment(a1SlipHead);
12971                    layoutSlip.setSignalA1Name(signalHeadName);
12972                }
12973                //} else if (assigned != B1) {
12974                //need to figure out what to do in this case - assigned to a different position on the same turnout.
12975            }
12976        }
12977
12978        signalHeadName = a2SlipSignalHeadComboBox.getSelectedItemDisplayName();
12979        if (signalHeadName == null) {
12980            signalHeadName = "";
12981        }
12982        if ((a2SlipHead != null) && setA2SlipHead.isSelected()) {
12983            if (isHeadOnPanel(a2SlipHead)
12984                    && (a2SlipHead != getHeadFromName(layoutSlip.getSignalB2Name()))) {
12985                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12986                        Bundle.getMessage("SignalsError6",
12987                                new Object[]{signalHeadName}),
12988                        Bundle.getMessage("ErrorTitle"),
12989                        JmriJOptionPane.ERROR_MESSAGE);
12990                return;
12991            } else {
12992                removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
12993                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
12994                    placeA2Slip(signalHeadName);
12995                } else {
12996                    placeB2Slip(signalHeadName);
12997                }
12998                removeAssignment(a2SlipHead);
12999                layoutSlip.setSignalA2Name(signalHeadName);
13000                needRedraw = true;
13001            }
13002        } else if (a2SlipHead != null) {
13003            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a2SlipHead, layoutSlip);
13004            if (assigned == LayoutTurnout.Geometry.NONE) {
13005                if (isHeadOnPanel(a2SlipHead)
13006                        && isHeadAssignedAnywhere(a2SlipHead)) {
13007                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13008                            Bundle.getMessage("SignalsError8",
13009                                    new Object[]{signalHeadName}),
13010                            Bundle.getMessage("ErrorTitle"),
13011                            JmriJOptionPane.ERROR_MESSAGE);
13012                    return;
13013                } else {
13014                    removeSignalHeadFromPanel(layoutSlip.getSignalA2Name());
13015                    removeAssignment(a2SlipHead);
13016                    layoutSlip.setSignalA2Name(signalHeadName);
13017                }
13018                //} else if (assigned != B2) {
13019                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13020            }
13021        } else { //a2SlipHead known to be null here
13022            removeSignalHeadFromPanel(layoutSlip.getSignalA2Name());
13023            layoutSlip.setSignalB2Name("");
13024        }
13025
13026        signalHeadName = b1SlipSignalHeadComboBox.getSelectedItemDisplayName();
13027        if (signalHeadName == null) {
13028            signalHeadName = "";
13029        }
13030        if (setB1SlipHead.isSelected()) {
13031            if (isHeadOnPanel(b1SlipHead)
13032                    && (b1SlipHead != getHeadFromName(layoutSlip.getSignalC1Name()))) {
13033                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13034                        Bundle.getMessage("SignalsError6",
13035                                new Object[]{signalHeadName}),
13036                        Bundle.getMessage("ErrorTitle"),
13037                        JmriJOptionPane.ERROR_MESSAGE);
13038                return;
13039            } else {
13040                removeSignalHeadFromPanel(layoutSlip.getSignalB1Name());
13041                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13042                    placeB1Slip(signalHeadName);
13043                } else {
13044                    placeA1Slip(signalHeadName);
13045                }
13046                removeAssignment(b1SlipHead);
13047                layoutSlip.setSignalB1Name(signalHeadName);
13048                needRedraw = true;
13049            }
13050        } else {
13051            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b1SlipHead, layoutSlip);
13052            if (assigned == LayoutTurnout.Geometry.NONE) {
13053                if (isHeadOnPanel(b1SlipHead)
13054                        && isHeadAssignedAnywhere(b1SlipHead)) {
13055                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13056                            Bundle.getMessage("SignalsError8",
13057                                    new Object[]{signalHeadName}),
13058                            Bundle.getMessage("ErrorTitle"),
13059                            JmriJOptionPane.ERROR_MESSAGE);
13060                    return;
13061                } else {
13062                    removeSignalHeadFromPanel(layoutSlip.getSignalB1Name());
13063                    removeAssignment(b1SlipHead);
13064                    layoutSlip.setSignalB1Name(signalHeadName);
13065                }
13066                //} else if (assigned != C1) {
13067                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13068            }
13069        }
13070
13071        if (layoutSlip.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
13072            signalHeadName = b2SlipSignalHeadComboBox.getSelectedItemDisplayName();
13073            if (signalHeadName == null) {
13074                signalHeadName = "";
13075            }
13076            if ((b2SlipHead != null) && setB2SlipHead.isSelected()) {
13077                if (isHeadOnPanel(b2SlipHead)
13078                        && (b2SlipHead != getHeadFromName(layoutSlip.getSignalC2Name()))) {
13079                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13080                            Bundle.getMessage("SignalsError6",
13081                                    new Object[]{signalHeadName}),
13082                            Bundle.getMessage("ErrorTitle"),
13083                            JmriJOptionPane.ERROR_MESSAGE);
13084                    return;
13085                } else {
13086                    removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13087                    if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13088                        placeB2Slip(signalHeadName);
13089                    } else {
13090                        placeA2Slip(signalHeadName);
13091                    }
13092                    removeAssignment(b2SlipHead);
13093                    layoutSlip.setSignalB2Name(signalHeadName);
13094                    needRedraw = true;
13095                }
13096            } else if (b2SlipHead != null) {
13097                LayoutTurnout.Geometry assigned = isHeadAssignedHere(b2SlipHead, layoutSlip);
13098                if (assigned == LayoutTurnout.Geometry.NONE) {
13099                    if (isHeadOnPanel(b2SlipHead)
13100                            && isHeadAssignedAnywhere(b2SlipHead)) {
13101                        JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13102                                Bundle.getMessage("SignalsError8",
13103                                        new Object[]{signalHeadName}),
13104                                Bundle.getMessage("ErrorTitle"),
13105                                JmriJOptionPane.ERROR_MESSAGE);
13106                        return;
13107                    } else {
13108                        removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13109                        removeAssignment(b2SlipHead);
13110                        layoutSlip.setSignalB2Name(signalHeadName);
13111                    }
13112                    //} else if (assigned != C2) {
13113                    //need to figure out what to do in this case - assigned to a different position on the same turnout.
13114                }
13115            } else { //b2SlipHead known to be null here
13116                removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13117                layoutSlip.setSignalB2Name("");
13118            }
13119        } else {
13120            if (b2SlipHead != null) {
13121                BlockBossLogic.getStoppedObject(layoutSlip.getSignalB2Name());
13122                removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13123                layoutSlip.setSignalB2Name("");
13124                b2SlipHead = null;
13125            }
13126        }
13127
13128        //signal heads on turnout 2
13129        signalHeadName = c1SlipSignalHeadComboBox.getSelectedItemDisplayName();
13130        if (signalHeadName == null) {
13131            signalHeadName = "";
13132        }
13133        if (setC1SlipHead.isSelected()) {
13134            if (isHeadOnPanel(c1SlipHead)
13135                    && (c1SlipHead != getHeadFromName(layoutSlip.getSignalB1Name()))) {
13136                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13137                        Bundle.getMessage("SignalsError6",
13138                                new Object[]{signalHeadName}),
13139                        Bundle.getMessage("ErrorTitle"),
13140                        JmriJOptionPane.ERROR_MESSAGE);
13141                return;
13142            } else {
13143                removeSignalHeadFromPanel(layoutSlip.getSignalC1Name());
13144                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13145                    placeC1Slip(signalHeadName);
13146                } else {
13147                    placeD1Slip(signalHeadName);
13148                }
13149                removeAssignment(c1SlipHead);
13150                layoutSlip.setSignalC1Name(signalHeadName);
13151                needRedraw = true;
13152            }
13153        } else {
13154            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c1SlipHead, layoutSlip);
13155            if (assigned == LayoutTurnout.Geometry.NONE) {
13156                if (isHeadOnPanel(c1SlipHead)
13157                        && isHeadAssignedAnywhere(c1SlipHead)) {
13158                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13159                            Bundle.getMessage("SignalsError8",
13160                                    new Object[]{signalHeadName}),
13161                            Bundle.getMessage("ErrorTitle"),
13162                            JmriJOptionPane.ERROR_MESSAGE);
13163                    return;
13164                } else {
13165                    removeSignalHeadFromPanel(layoutSlip.getSignalC1Name());
13166                    removeAssignment(c1SlipHead);
13167                    layoutSlip.setSignalC1Name(signalHeadName);
13168                }
13169                //} else if (assigned != B1) {
13170                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13171            }
13172        }
13173
13174        if (layoutSlip.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
13175            signalHeadName = c2SlipSignalHeadComboBox.getSelectedItemDisplayName();
13176            if (signalHeadName == null) {
13177                signalHeadName = "";
13178            }
13179            if ((c2SlipHead != null) && setC2SlipHead.isSelected()) {
13180                if (isHeadOnPanel(c2SlipHead)
13181                        && (c2SlipHead != getHeadFromName(layoutSlip.getSignalB2Name()))) {
13182                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13183                            Bundle.getMessage("SignalsError6",
13184                                    new Object[]{signalHeadName}),
13185                            Bundle.getMessage("ErrorTitle"),
13186                            JmriJOptionPane.ERROR_MESSAGE);
13187                    return;
13188                } else {
13189                    removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13190                    if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13191                        placeC2Slip(signalHeadName);
13192                    } else {
13193                        placeD2Slip(signalHeadName);
13194                    }
13195                    removeAssignment(c2SlipHead);
13196                    layoutSlip.setSignalC2Name(signalHeadName);
13197                    needRedraw = true;
13198                }
13199            } else if (c2SlipHead != null) {
13200                LayoutTurnout.Geometry assigned = isHeadAssignedHere(c2SlipHead, layoutSlip);
13201                if (assigned == LayoutTurnout.Geometry.NONE) {
13202                    if (isHeadOnPanel(c2SlipHead)
13203                            && isHeadAssignedAnywhere(c2SlipHead)) {
13204                        JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13205                                Bundle.getMessage("SignalsError8",
13206                                        new Object[]{signalHeadName}),
13207                                Bundle.getMessage("ErrorTitle"),
13208                                JmriJOptionPane.ERROR_MESSAGE);
13209                        return;
13210                    } else {
13211                        removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13212                        removeAssignment(c2SlipHead);
13213                        layoutSlip.setSignalC2Name(signalHeadName);
13214                    }
13215                    //} else if (assigned != B2) {
13216                    //need to figure out what to do in this case - assigned to a different position on the same turnout.
13217                }
13218            } else { //c2SlipHead known to be null here
13219                removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13220                layoutSlip.setSignalC2Name("");
13221            }
13222        } else {
13223            if (c2SlipHead != null) {
13224                BlockBossLogic.getStoppedObject(layoutSlip.getSignalC2Name());
13225                removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13226                layoutSlip.setSignalC2Name("");
13227                c2SlipHead = null;
13228            }
13229        }
13230
13231        signalHeadName = d1SlipSignalHeadComboBox.getSelectedItemDisplayName();
13232        if (signalHeadName == null) {
13233            signalHeadName = "";
13234        }
13235        if (setD1SlipHead.isSelected()) {
13236            if (isHeadOnPanel(d1SlipHead)
13237                    && (d1SlipHead != getHeadFromName(layoutSlip.getSignalC1Name()))) {
13238                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13239                        Bundle.getMessage("SignalsError6",
13240                                new Object[]{signalHeadName}),
13241                        Bundle.getMessage("ErrorTitle"),
13242                        JmriJOptionPane.ERROR_MESSAGE);
13243                return;
13244            } else {
13245                removeSignalHeadFromPanel(layoutSlip.getSignalD1Name());
13246                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13247                    placeD1Slip(signalHeadName);
13248                } else {
13249                    placeC1Slip(signalHeadName);
13250                }
13251                removeAssignment(d1SlipHead);
13252                layoutSlip.setSignalD1Name(signalHeadName);
13253                needRedraw = true;
13254            }
13255        } else {
13256            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d1SlipHead, layoutSlip);
13257            if (assigned == LayoutTurnout.Geometry.NONE) {
13258                if (isHeadOnPanel(d1SlipHead)
13259                        && isHeadAssignedAnywhere(d1SlipHead)) {
13260                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13261                            Bundle.getMessage("SignalsError8",
13262                                    new Object[]{signalHeadName}),
13263                            Bundle.getMessage("ErrorTitle"),
13264                            JmriJOptionPane.ERROR_MESSAGE);
13265                    return;
13266                } else {
13267                    removeSignalHeadFromPanel(layoutSlip.getSignalD1Name());
13268                    removeAssignment(d1SlipHead);
13269                    layoutSlip.setSignalD1Name(signalHeadName);
13270                }
13271                //} else if (assigned != C1) {
13272                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13273            }
13274        }
13275
13276        signalHeadName = d2SlipSignalHeadComboBox.getSelectedItemDisplayName();
13277        if (signalHeadName == null) {
13278            signalHeadName = "";
13279        }
13280        if ((d2SlipHead != null) && setD2SlipHead.isSelected()) {
13281            if (isHeadOnPanel(d2SlipHead)
13282                    && (d2SlipHead != getHeadFromName(layoutSlip.getSignalC2Name()))) {
13283                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13284                        Bundle.getMessage("SignalsError6",
13285                                new Object[]{signalHeadName}),
13286                        Bundle.getMessage("ErrorTitle"),
13287                        JmriJOptionPane.ERROR_MESSAGE);
13288                return;
13289            } else {
13290                removeSignalHeadFromPanel(layoutSlip.getSignalD2Name());
13291                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13292                    placeD2Slip(signalHeadName);
13293                } else {
13294                    placeC2Slip(signalHeadName);
13295                }
13296                removeAssignment(d2SlipHead);
13297                layoutSlip.setSignalD2Name(signalHeadName);
13298                needRedraw = true;
13299            }
13300        } else if (d2SlipHead != null) {
13301            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d2SlipHead, layoutSlip);
13302            if (assigned == LayoutTurnout.Geometry.NONE) {
13303                if (isHeadOnPanel(d2SlipHead)
13304                        && isHeadAssignedAnywhere(d2SlipHead)) {
13305                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13306                            Bundle.getMessage("SignalsError8",
13307                                    new Object[]{signalHeadName}),
13308                            Bundle.getMessage("ErrorTitle"),
13309                            JmriJOptionPane.ERROR_MESSAGE);
13310                    return;
13311                } else {
13312                    removeSignalHeadFromPanel(layoutSlip.getSignalD2Name());
13313                    removeAssignment(d2SlipHead);
13314                    layoutSlip.setSignalD2Name(signalHeadName);
13315                }
13316                //} else if (assigned != C2) {
13317                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13318            }
13319        } else { //d2SlipHead known to be null here
13320            removeSignalHeadFromPanel(layoutSlip.getSignalD2Name());
13321            layoutSlip.setSignalD2Name("");
13322        }
13323        //setup logic if requested
13324        if (setupA1SlipLogic.isSelected() || setupA2SlipLogic.isSelected()) {
13325            setLogicSlip(a1SlipHead, (TrackSegment) layoutSlip.getConnectC(), a2SlipHead,
13326                    (TrackSegment) layoutSlip.getConnectD(), setupA1SlipLogic.isSelected(),
13327                    setupA2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnout(),
13328                    layoutSlip.getTurnoutB(), LayoutTurnout.STATE_AC, LayoutTurnout.STATE_AD, 0);
13329        }
13330        if (setupB1SlipLogic.isSelected() || setupB2SlipLogic.isSelected()) {
13331            setLogicSlip(b1SlipHead, (TrackSegment) layoutSlip.getConnectD(), b2SlipHead,
13332                    (TrackSegment) layoutSlip.getConnectC(), setupB1SlipLogic.isSelected(),
13333                    setupB2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnout(),
13334                    layoutSlip.getTurnoutB(), LayoutTurnout.STATE_BD, LayoutTurnout.STATE_BC, 2);
13335        }
13336        if (setupC1SlipLogic.isSelected() || setupC2SlipLogic.isSelected()) {
13337            setLogicSlip(c1SlipHead, (TrackSegment) layoutSlip.getConnectA(), c2SlipHead,
13338                    (TrackSegment) layoutSlip.getConnectB(), setupC1SlipLogic.isSelected(),
13339                    setupC2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnoutB(),
13340                    layoutSlip.getTurnout(), LayoutTurnout.STATE_AC, LayoutTurnout.STATE_BC, 4);
13341        }
13342        if (setupD1SlipLogic.isSelected() || setupD2SlipLogic.isSelected()) {
13343            setLogicSlip(d1SlipHead, (TrackSegment) layoutSlip.getConnectB(), d2SlipHead,
13344                    (TrackSegment) layoutSlip.getConnectA(), setupD1SlipLogic.isSelected(),
13345                    setupD2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnoutB(),
13346                    layoutSlip.getTurnout(), LayoutTurnout.STATE_BD, LayoutTurnout.STATE_AD, 6);
13347        }
13348        //finish up
13349        setSignalsAtSlipOpenFlag = false;
13350        setSignalsAtSlipFrame.setVisible(false);
13351
13352        if (needRedraw) {
13353            layoutEditor.redrawPanel();
13354            needRedraw = false;
13355            layoutEditor.setDirty();
13356        }
13357    }   //setSlipSignalsDonePressed
13358
13359    private boolean getSlipSignalHeadInformation() {
13360        a1SlipHead = getSignalHeadFromEntry(a1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13361        if (a1SlipHead == null) {
13362            return false;
13363        }
13364        a2SlipHead = getSignalHeadFromEntry(a2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13365
13366        b1SlipHead = getSignalHeadFromEntry(b1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13367        if (b1SlipHead == null) {
13368            return false;
13369        }
13370        b2SlipHead = getSignalHeadFromEntry(b2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13371
13372        c1SlipHead = getSignalHeadFromEntry(c1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13373        if (c1SlipHead == null) {
13374            return false;
13375        }
13376        c2SlipHead = getSignalHeadFromEntry(c2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13377
13378        d1SlipHead = getSignalHeadFromEntry(d1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13379        if (d1SlipHead == null) {
13380            return false;
13381        }
13382        d2SlipHead = getSignalHeadFromEntry(d2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13383
13384        return true;
13385    }
13386
13387    private void placeA1Slip(String signalHeadName) {
13388        //place head near the continuing track of turnout 1
13389        //placingBlock(getSignalHeadIcon(signalHeadName), false, 0.0, layoutSlip.getConnectA(), layoutSlip.getCoordsA());
13390        if (testIcon == null) {
13391            testIcon = signalIconEditor.getIcon(0);
13392        }
13393        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13394
13395        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13396        Point2D coordsA = layoutSlipView.getCoordsA();
13397        Point2D coordsD = layoutSlipView.getCoordsD();
13398        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13399
13400        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
13401        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13402        double diffDirDEG = MathUtil.diffAngleDEG(aDirDEG, dDirDEG);
13403        double shiftX = 0.0;
13404        if (diffDirDEG < 0.0) {
13405            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13406        }
13407        Point2D delta = new Point2D.Double(shiftX, -shift);
13408
13409        delta = MathUtil.rotateDEG(delta, aDirDEG);
13410        Point2D where = MathUtil.add(coordsA, delta);
13411        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
13412    }
13413
13414    private void placeA2Slip(String signalHeadName) {
13415        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13416        //placingBlock(l, false, (4 + l.getHeight()), layoutSlip.getConnectA(), layoutSlip.getCoordsA());
13417        if (testIcon == null) {
13418            testIcon = signalIconEditor.getIcon(0);
13419        }
13420        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13421
13422        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13423        Point2D coordsA = layoutSlipView.getCoordsA();
13424        Point2D coordsD = layoutSlipView.getCoordsD();
13425        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13426
13427        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
13428        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13429        double diffDirDEG = MathUtil.diffAngleDEG(aDirDEG, dDirDEG);
13430        double shiftX = 2.0 * shift;
13431        if (diffDirDEG < 0.0) {
13432            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13433        }
13434        Point2D delta = new Point2D.Double(shiftX, -shift);
13435
13436        delta = MathUtil.rotateDEG(delta, aDirDEG);
13437        Point2D where = MathUtil.add(coordsA, delta);
13438        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
13439    }
13440
13441    private void placeB1Slip(String signalHeadName) {
13442        //placingBlock(getSignalHeadIcon(signalHeadName), true, 0.0, layoutSlip.getConnectB(), layoutSlip.getCoordsB());
13443        if (testIcon == null) {
13444            testIcon = signalIconEditor.getIcon(0);
13445        }
13446        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13447
13448        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13449        Point2D coordsB = layoutSlipView.getCoordsB();
13450        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13451
13452        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13453
13454        Point2D delta = new Point2D.Double(+shift, -shift);
13455        delta = MathUtil.rotateDEG(delta, bDirDEG);
13456        Point2D where = MathUtil.add(coordsB, delta);
13457        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
13458    }
13459
13460    private void placeB2Slip(String signalHeadName) {
13461        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13462        //placingBlock(l, true, (4 + l.getHeight()), layoutSlip.getConnectB(), layoutSlip.getCoordsB());
13463        if (testIcon == null) {
13464            testIcon = signalIconEditor.getIcon(0);
13465        }
13466        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13467
13468        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13469        Point2D coordsB = layoutSlipView.getCoordsB();
13470        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13471
13472        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13473
13474        Point2D delta = new Point2D.Double(-shift, -shift);
13475        delta = MathUtil.rotateDEG(delta, bDirDEG);
13476        Point2D where = MathUtil.add(coordsB, delta);
13477        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
13478    }
13479
13480    private void placeC1Slip(String signalHeadName) {
13481        //placingBlock(getSignalHeadIcon(signalHeadName), false, 0.0, layoutSlip.getConnectC(), layoutSlip.getCoordsC());
13482        if (testIcon == null) {
13483            testIcon = signalIconEditor.getIcon(0);
13484        }
13485        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13486
13487        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13488        Point2D coordsB = layoutSlipView.getCoordsB();
13489        Point2D coordsC = layoutSlipView.getCoordsC();
13490        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13491
13492        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13493        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
13494        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
13495        double shiftX = 0.0;
13496        if (diffDirDEG < 0.0) {
13497            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13498        }
13499        Point2D delta = new Point2D.Double(shiftX, -shift);
13500
13501        delta = MathUtil.rotateDEG(delta, cDirDEG);
13502        Point2D where = MathUtil.add(coordsC, delta);
13503        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
13504    }
13505
13506    private void placeC2Slip(String signalHeadName) {
13507        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13508        //placingBlock(l, false, (4 + l.getHeight()), layoutSlip.getConnectC(), layoutSlip.getCoordsC());
13509        if (testIcon == null) {
13510            testIcon = signalIconEditor.getIcon(0);
13511        }
13512        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13513
13514        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13515        Point2D coordsB = layoutSlipView.getCoordsB();
13516        Point2D coordsC = layoutSlipView.getCoordsC();
13517        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13518
13519        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13520        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
13521        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
13522        double shiftX = 2.0 * shift;
13523        if (diffDirDEG < 0.0) {
13524            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13525        }
13526        Point2D delta = new Point2D.Double(shiftX, -shift);
13527
13528        delta = MathUtil.rotateDEG(delta, cDirDEG);
13529        Point2D where = MathUtil.add(coordsC, delta);
13530        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
13531    }
13532
13533    private void placeD1Slip(String signalHeadName) {
13534        //placingBlock(getSignalHeadIcon(signalHeadName), true, 0.0, layoutSlip.getConnectD(), layoutSlip.getCoordsD());
13535        if (testIcon == null) {
13536            testIcon = signalIconEditor.getIcon(0);
13537        }
13538        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13539
13540        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13541        Point2D coordsD = layoutSlipView.getCoordsD();
13542        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13543
13544        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13545
13546        Point2D delta = new Point2D.Double(+shift, -shift);
13547        delta = MathUtil.rotateDEG(delta, dDirDEG);
13548        Point2D where = MathUtil.add(coordsD, delta);
13549        setSignalHeadOnPanel(dDirDEG, signalHeadName, where);
13550    }
13551
13552    private void placeD2Slip(String signalHeadName) {
13553        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13554        //placingBlock(l, true, (4 + l.getHeight()), layoutSlip.getConnectD(), layoutSlip.getCoordsD());
13555        if (testIcon == null) {
13556            testIcon = signalIconEditor.getIcon(0);
13557        }
13558        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13559
13560        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13561        Point2D coordsD = layoutSlipView.getCoordsD();
13562        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13563
13564        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13565
13566        Point2D delta = new Point2D.Double(-shift, -shift);
13567        delta = MathUtil.rotateDEG(delta, dDirDEG);
13568        Point2D where = MathUtil.add(coordsD, delta);
13569        setSignalHeadOnPanel(dDirDEG, signalHeadName, where);
13570    }
13571
13572    private void setLogicSlip(SignalHead head, TrackSegment track1, SignalHead secondHead, TrackSegment track2,
13573            boolean setup1, boolean setup2,
13574            LayoutSlip slip, Turnout nearTurnout, Turnout farTurnout,
13575            int continueState, int divergeState, int number) {
13576        //initialize common components and ensure all is defined
13577        LayoutBlock connectorBlock = slip.getLayoutBlock();
13578        Sensor connectorOccupancy = null;
13579        if (connectorBlock == null) {
13580            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13581                    Bundle.getMessage("InfoMessage6"),
13582                    Bundle.getMessage("MessageTitle"),
13583                    JmriJOptionPane.INFORMATION_MESSAGE);
13584            return;
13585        }
13586        connectorOccupancy = connectorBlock.getOccupancySensor();
13587        if (connectorOccupancy == null) {
13588            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13589                    Bundle.getMessage("InfoMessage4",
13590                            new Object[]{connectorBlock.getUserName()}),
13591                    Bundle.getMessage("MessageTitle"),
13592                    JmriJOptionPane.INFORMATION_MESSAGE);
13593            return;
13594        }
13595
13596        int nearState = layoutSlip.getTurnoutState(nearTurnout, continueState);
13597        int farState = layoutSlip.getTurnoutState(farTurnout, continueState);
13598
13599        //setup signal head for continuing track of far turnout (or both tracks of far turnout)
13600        if ((track1 == null) && setup1) {
13601            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13602                    Bundle.getMessage("InfoMessage7"),
13603                    Bundle.getMessage("MessageTitle"),
13604                    JmriJOptionPane.INFORMATION_MESSAGE);
13605            return;
13606        }
13607        Sensor occupancy = null;
13608        SignalHead nextHead = null;
13609        if ((track1 != null) && setup1) {
13610            LayoutBlock block = track1.getLayoutBlock();
13611            if (block == null) {
13612                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13613                        Bundle.getMessage("InfoMessage6"),
13614                        Bundle.getMessage("MessageTitle"),
13615                        JmriJOptionPane.INFORMATION_MESSAGE);
13616                return;
13617            }
13618            occupancy = block.getOccupancySensor();
13619            if (occupancy == null) {
13620                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13621                        Bundle.getMessage("InfoMessage4",
13622                                new Object[]{block.getUserName()}),
13623                        Bundle.getMessage("MessageTitle"),
13624                        JmriJOptionPane.INFORMATION_MESSAGE);
13625                return;
13626            }
13627            //need to sort this out???
13628            nextHead = getNextSignalFromObject(track1, slip,
13629                    head.getSystemName(), setSignalsAtSlipFrame);
13630            if ((nextHead == null) && (!reachedEndBumper())) {
13631                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13632                        Bundle.getMessage("InfoMessage5",
13633                                new Object[]{block.getUserName()}),
13634                        Bundle.getMessage("MessageTitle"),
13635                        JmriJOptionPane.INFORMATION_MESSAGE);
13636                return;
13637            }
13638            if (secondHead != null) {
13639                //this head signals only the continuing track of the far turnout
13640                if (!initializeBlockBossLogic(head.getDisplayName())) {
13641                    return;
13642                }
13643                logic.setMode(BlockBossLogic.TRAILINGMAIN);
13644                if (farState == Turnout.THROWN) {
13645                    logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
13646                }
13647                logic.setTurnout(farTurnout.getDisplayName());
13648                logic.setSensor1(occupancy.getDisplayName());
13649                if (occupancy != connectorOccupancy) {
13650                    logic.setSensor2(connectorOccupancy.getDisplayName());
13651                }
13652                if (nextHead != null) {
13653                    logic.setWatchedSignal1(nextHead.getDisplayName(), false);
13654                }
13655                if (auxSignal != null) {
13656                    logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
13657                }
13658                String nearSensorName = setupNearLogixSlip(nearTurnout, nearState, head, farTurnout, farState, slip, number);
13659                addNearSensorToSlipLogic(nearSensorName);
13660                finalizeBlockBossLogic();
13661            }
13662        }
13663        if ((secondHead != null) && !setup2) {
13664            return;
13665        }
13666        SignalHead savedAuxSignal = auxSignal;
13667        if (track2 == null) {
13668            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13669                    Bundle.getMessage("InfoMessage7"),
13670                    Bundle.getMessage("MessageTitle"),
13671                    JmriJOptionPane.INFORMATION_MESSAGE);
13672            return;
13673        }
13674        LayoutBlock block2 = track2.getLayoutBlock();
13675        if (block2 == null) {
13676            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13677                    Bundle.getMessage("InfoMessage6"),
13678                    Bundle.getMessage("MessageTitle"),
13679                    JmriJOptionPane.INFORMATION_MESSAGE);
13680            return;
13681        }
13682        Sensor occupancy2 = block2.getOccupancySensor();
13683        if (occupancy2 == null) {
13684            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13685                    Bundle.getMessage("InfoMessage4",
13686                            new Object[]{block2.getUserName()}),
13687                    Bundle.getMessage("MessageTitle"),
13688                    JmriJOptionPane.INFORMATION_MESSAGE);
13689            return;
13690        }
13691        SignalHead nextHead2 = null;
13692        if (secondHead != null) {
13693            nextHead2 = getNextSignalFromObject(track2,
13694                    slip, secondHead.getSystemName(), setSignalsAtSlipFrame);
13695            if ((nextHead2 == null) && (!reachedEndBumper())) {
13696                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13697                        Bundle.getMessage("InfoMessage5",
13698                                new Object[]{block2.getUserName()}),
13699                        Bundle.getMessage("MessageTitle"),
13700                        JmriJOptionPane.INFORMATION_MESSAGE);
13701                return;
13702            }
13703        }
13704        if ((secondHead == null) && (track1 != null) && setup1) {
13705            if (!initializeBlockBossLogic(head.getDisplayName())) {
13706                return;
13707            }
13708            logic.setMode(BlockBossLogic.FACING);
13709            logic.setTurnout(farTurnout.getDisplayName());
13710            if (occupancy != null) {
13711                logic.setWatchedSensor1(occupancy.getDisplayName());
13712            }
13713            logic.setWatchedSensor2(occupancy2.getDisplayName());
13714            logic.setSensor2(connectorOccupancy.getDisplayName());
13715            if (nextHead != null) {
13716                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
13717            }
13718            if (savedAuxSignal != null) {
13719                logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
13720            }
13721            if (nextHead2 != null) {
13722                logic.setWatchedSignal2(nextHead2.getDisplayName());
13723            }
13724            if (auxSignal != null) {
13725                logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
13726            }
13727            String nearSensorName = setupNearLogixSlip(nearTurnout, nearState, head, farTurnout, farState, slip, number + 1);
13728            addNearSensorToSlipLogic(nearSensorName);
13729            logic.setLimitSpeed2(true);
13730            finalizeBlockBossLogic();
13731        } else if ((secondHead != null) && setup2) {
13732            if (!initializeBlockBossLogic(secondHead.getDisplayName())) {
13733                return;
13734            }
13735            nearState = layoutSlip.getTurnoutState(nearTurnout, divergeState);
13736            farState = layoutSlip.getTurnoutState(farTurnout, divergeState);
13737
13738            logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
13739            if (farState == Turnout.CLOSED) {
13740                logic.setMode(BlockBossLogic.TRAILINGMAIN);
13741                logic.setLimitSpeed1(true);
13742            } else {
13743                logic.setLimitSpeed2(true);
13744            }
13745            logic.setTurnout(farTurnout.getDisplayName());
13746            logic.setSensor1(occupancy2.getDisplayName());
13747            if (occupancy2 != connectorOccupancy) {
13748                logic.setSensor2(connectorOccupancy.getDisplayName());
13749            }
13750            if (nextHead2 != null) {
13751                logic.setWatchedSignal1(nextHead2.getDisplayName(), false);
13752            }
13753            if (auxSignal != null) {
13754                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
13755            }
13756            String nearSensorName = setupNearLogixSlip(nearTurnout, nearState, secondHead, farTurnout, farState, slip, number + 1);
13757            addNearSensorToSlipLogic(nearSensorName);
13758            finalizeBlockBossLogic();
13759        }
13760    }   //setLogicSlip
13761
13762    private String setupNearLogixSlip(Turnout turn, int nearState,
13763            SignalHead head, Turnout farTurn, int farState, LayoutSlip slip, int number) {
13764        String turnoutName = turn.getDisplayName();
13765        String farTurnoutName = farTurn.getDisplayName();
13766
13767        String logixPrefix = InstanceManager.getDefault(jmri.LogixManager.class).getSystemNamePrefix();
13768        String pref = InstanceManager.getDefault(jmri.LogixManager.class).getSystemPrefix();
13769        String logixName = logixPrefix + ":IX_LAYOUTSLIP:" + slip.getId();
13770        String sensorName = pref + "S:" + logixName + "C" + number;
13771        try {
13772            InstanceManager.sensorManagerInstance().provideSensor(sensorName);
13773        } catch (IllegalArgumentException ex) {
13774            log.error("Trouble creating sensor {} while setting up Logix.", sensorName);
13775            return "";
13776        }
13777        boolean newConditional = false;
13778        Logix x = InstanceManager.getDefault(LogixManager.class
13779        ).getBySystemName(logixName);
13780
13781        if (x == null) {
13782            x = InstanceManager.getDefault(LogixManager.class
13783            ).createNewLogix(logixName, "");
13784            newConditional = true;
13785            if (x == null) {
13786                log.error("Trouble creating logix {} while setting up signal logic.", logixName);
13787                return "";
13788            }
13789            x.setComment("Layout Slip, Signalhead logic");
13790        }
13791        x.deActivateLogix();
13792        String cName = logixName + "C" + number;
13793
13794        Conditional c = InstanceManager.getDefault(ConditionalManager.class
13795        ).getBySystemName(cName);
13796
13797        if (c == null) {
13798            c = InstanceManager.getDefault(ConditionalManager.class
13799            ).
13800                    createNewConditional(cName, "");
13801            newConditional = true;
13802            if (c == null) {
13803                log.error("Trouble creating conditional {} while setting up Logix.", cName);
13804                return "";
13805            }
13806        }
13807        Conditional.Type type = Conditional.Type.TURNOUT_THROWN;
13808        if (nearState == Turnout.CLOSED) {
13809            type = Conditional.Type.TURNOUT_CLOSED;
13810        }
13811        ArrayList<ConditionalVariable> variableList = new ArrayList<>();
13812        variableList.add(new ConditionalVariable(false, Conditional.Operator.AND,
13813                type, turnoutName, true));
13814
13815        type = Conditional.Type.TURNOUT_THROWN;
13816        if (farState == Turnout.CLOSED) {
13817            type = Conditional.Type.TURNOUT_CLOSED;
13818        }
13819        variableList.add(new ConditionalVariable(false, Conditional.Operator.AND,
13820                type, farTurnoutName, true));
13821        c.setStateVariables(variableList);
13822        ArrayList<ConditionalAction> actionList = new ArrayList<>();
13823        actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
13824                Conditional.Action.SET_SENSOR, sensorName,
13825                Sensor.INACTIVE, ""));
13826        actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE,
13827                Conditional.Action.SET_SENSOR, sensorName,
13828                Sensor.ACTIVE, ""));
13829        c.setAction(actionList); //string data
13830        if (newConditional) {
13831            x.addConditional(cName, -1);
13832        }
13833        x.activateLogix();
13834        return sensorName;
13835    }   //setupNearLogixSlip
13836
13837    /*
13838     * Adds the sensor specified to the open BlockBossLogic, provided it is not already there and
13839     * provided there is an open slot. If 'name' is null or empty, returns without doing anything.
13840     */
13841    private void addNearSensorToSlipLogic(String name) {
13842        if ((name != null) && !name.isEmpty()) {
13843            //return if a sensor by this name is already present
13844            if (logic.getSensor1() != null && logic.getSensor1().equals(name)) {
13845                return;
13846            }
13847            if (logic.getSensor2() != null && logic.getSensor2().equals(name)) {
13848                return;
13849            }
13850            if (logic.getSensor3() != null && logic.getSensor3().equals(name)) {
13851                return;
13852            }
13853            if (logic.getSensor4() != null && logic.getSensor4().equals(name)) {
13854                return;
13855            }
13856            if (logic.getSensor5() != null && logic.getSensor5().equals(name)) {
13857                return;
13858            }
13859            //add in the first available slot
13860            if (logic.getSensor1() == null) {
13861                logic.setSensor1(name);
13862            } else if (logic.getSensor2() == null) {
13863                logic.setSensor2(name);
13864            } else if (logic.getSensor3() == null) {
13865                logic.setSensor3(name);
13866            } else if (logic.getSensor4() == null) {
13867                logic.setSensor4(name);
13868            } else if (logic.getSensor5() == null) {
13869                logic.setSensor5(name);
13870            } else {
13871                log.error("Error - could not add sensor to SSL for signal head {}", logic.getDrivenSignal());
13872            }
13873        }
13874    }
13875
13876    /**
13877     * get a signal head icon for the given signal head
13878     *
13879     * @param signalName name of a signal head.
13880     * @return a SignalHeadIcon for the signal.
13881     */
13882    @CheckReturnValue
13883    public SignalHeadIcon getSignalHeadIcon(@Nonnull String signalName) {
13884        if (signalIconEditor == null) {
13885            signalIconEditor = layoutEditor.getLayoutEditorToolBarPanel().signalIconEditor;
13886        }
13887        SignalHeadIcon l = new SignalHeadIcon(layoutEditor);
13888        l.setSignalHead(signalName);
13889        l.setIcon("SignalHeadStateRed", signalIconEditor.getIcon(0));
13890        l.setIcon("SignalHeadStateFlashingRed", signalIconEditor.getIcon(1));
13891        l.setIcon("SignalHeadStateYellow", signalIconEditor.getIcon(2));
13892        l.setIcon("SignalHeadStateFlashingYellow", signalIconEditor.getIcon(3));
13893        l.setIcon("SignalHeadStateGreen", signalIconEditor.getIcon(4));
13894        l.setIcon("SignalHeadStateFlashingGreen", signalIconEditor.getIcon(5));
13895        l.setIcon("SignalHeadStateDark", signalIconEditor.getIcon(6));
13896        l.setIcon("SignalHeadStateHeld", signalIconEditor.getIcon(7));
13897        l.setIcon("SignalHeadStateLunar", signalIconEditor.getIcon(8));
13898        l.setIcon("SignalHeadStateFlashingLunar", signalIconEditor.getIcon(9));
13899        l.rotate(90);
13900        return l;
13901    }
13902
13903    //convenience strings
13904    private final String eastString = Bundle.getMessage("East");
13905    private final String westString = Bundle.getMessage("West");
13906    private final String continuingString = Bundle.getMessage("Continuing");
13907    private final String divergingString = Bundle.getMessage("Diverging");
13908    private final String throatString = Bundle.getMessage("Throat");
13909    private final String throatContinuingString = Bundle.getMessage("ThroatContinuing");
13910    private final String throatDivergingString = Bundle.getMessage("ThroatDiverging");
13911
13912    private final String divergingAString = Bundle.getMessage("Diverging_", "A");
13913    private final String divergingBString = Bundle.getMessage("Diverging_", "B");
13914
13915    protected Boolean addLayoutTurnoutSignalHeadInfoToMenu(
13916            @Nonnull String inTurnoutNameA, @Nonnull String inTurnoutNameB,
13917            @Nonnull JMenu inMenu) {
13918        Boolean result = false; //assume failure (pessimist!)
13919
13920        //lookup turnouts
13921        turnout = turnout1 = turnoutA = InstanceManager.turnoutManagerInstance().getTurnout(inTurnoutNameA);
13922        turnout2 = turnoutB = InstanceManager.turnoutManagerInstance().getTurnout(inTurnoutNameB);
13923        //map those to layout turnouts (if possible)
13924        for (LayoutTurnout lt : layoutEditor.getLayoutTurnouts()) {
13925            Turnout to = lt.getTurnout();
13926            if (to != null) {
13927                String uname = to.getUserName();
13928                String sname = to.getSystemName();
13929                if (!inTurnoutNameA.isEmpty() && (sname.equals(inTurnoutNameA) || ((uname != null) && uname.equals(inTurnoutNameA)))) {
13930                    layoutTurnout = layoutTurnout1 = layoutTurnoutA = lt;
13931                }
13932                if (!inTurnoutNameB.isEmpty() && (sname.equals(inTurnoutNameB) || ((uname != null) && uname.equals(inTurnoutNameB)))) {
13933                    layoutTurnout2 = layoutTurnoutB = lt;
13934                }
13935            }
13936        }
13937
13938        int before_mcc = inMenu.getMenuComponentCount();
13939        if (before_mcc != 0) {
13940            inMenu.add(new JSeparator());
13941        }
13942        LayoutTurnout.LinkType linkType = layoutTurnout.getLinkType();
13943        if ((layoutTurnout.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
13944                || (layoutTurnout.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)
13945                || (layoutTurnout.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)) {
13946            JMenuItem jmi = inMenu.add(Bundle.getMessage("Crossover"));
13947            jmi.setEnabled(false);
13948            inMenu.add(new JSeparator());
13949            before_mcc += 2;
13950            addInfoToMenu("A " + continuingString, layoutTurnout.getSignalA1Name(), inMenu);
13951            addInfoToMenu("A " + divergingString, layoutTurnout.getSignalA2Name(), inMenu);
13952            addInfoToMenu("B " + continuingString, layoutTurnout.getSignalB1Name(), inMenu);
13953            addInfoToMenu("B " + divergingString, layoutTurnout.getSignalB2Name(), inMenu);
13954            addInfoToMenu("C " + continuingString, layoutTurnout.getSignalC1Name(), inMenu);
13955            addInfoToMenu("C " + divergingString, layoutTurnout.getSignalC2Name(), inMenu);
13956            addInfoToMenu("D " + continuingString, layoutTurnout.getSignalD1Name(), inMenu);
13957            addInfoToMenu("D " + divergingString, layoutTurnout.getSignalD2Name(), inMenu);
13958        } else if (linkType == LayoutTurnout.LinkType.NO_LINK) {
13959            JMenuItem jmi = inMenu.add(Bundle.getMessage("BeanNameTurnout"));
13960            jmi.setEnabled(false);
13961            inMenu.add(new JSeparator());
13962            before_mcc += 2;
13963            addInfoToMenu(throatContinuingString, layoutTurnout.getSignalA1Name(), inMenu);
13964            addInfoToMenu(throatDivergingString, layoutTurnout.getSignalA2Name(), inMenu);
13965            addInfoToMenu(continuingString, layoutTurnout.getSignalB1Name(), inMenu);
13966            addInfoToMenu(divergingString, layoutTurnout.getSignalC1Name(), inMenu);
13967        } else if (linkType == LayoutTurnout.LinkType.THROAT_TO_THROAT) {
13968            String menuString = Bundle.getMessage("ThroatToThroat") + " (";
13969            menuString += Bundle.getMessage("BeanNameTurnout") + ", " + Bundle.getMessage("BeanNameRoute");
13970            menuString += ", " + Bundle.getMessage("BeanNameSignalHead") + ":)";
13971            JMenuItem jmi = inMenu.add(menuString);
13972            jmi.setEnabled(false);
13973            inMenu.add(new JSeparator());
13974            before_mcc += 2;
13975            addInfoToMenu(eastString + ", " + continuingString + ", " + continuingString, layoutTurnout1.getSignalB1Name(), inMenu);
13976            addInfoToMenu(eastString + ", " + continuingString + ", " + divergingString, layoutTurnout1.getSignalB2Name(), inMenu);
13977            addInfoToMenu(eastString + ", " + divergingString + ", " + continuingString, layoutTurnout1.getSignalC1Name(), inMenu);
13978            addInfoToMenu(eastString + ", " + divergingString + ", " + divergingString, layoutTurnout1.getSignalC2Name(), inMenu);
13979            addInfoToMenu(westString + ", " + continuingString + ", " + continuingString, layoutTurnout2.getSignalB1Name(), inMenu);
13980            addInfoToMenu(westString + ", " + continuingString + ", " + divergingString, layoutTurnout2.getSignalB2Name(), inMenu);
13981            addInfoToMenu(westString + ", " + divergingString + ", " + continuingString, layoutTurnout2.getSignalC1Name(), inMenu);
13982            addInfoToMenu(westString + ", " + divergingString + ", " + divergingString, layoutTurnout2.getSignalC2Name(), inMenu);
13983        } else if (linkType == LayoutTurnout.LinkType.FIRST_3_WAY) {
13984            JMenuItem jmi = inMenu.add(Bundle.getMessage("ThreeWay"));
13985            jmi.setEnabled(false);
13986            inMenu.add(new JSeparator());
13987            before_mcc += 2;
13988            addInfoToMenu(throatString + " " + continuingString, layoutTurnoutA.getSignalA1Name(), inMenu);
13989            addInfoToMenu(throatString + " " + divergingAString, layoutTurnoutA.getSignalA2Name(), inMenu);
13990            addInfoToMenu(throatString + " " + divergingBString, layoutTurnoutA.getSignalA3Name(), inMenu);
13991            addInfoToMenu(continuingString, layoutTurnoutA.getSignalC1Name(), inMenu);
13992            addInfoToMenu(divergingAString, layoutTurnoutB.getSignalB1Name(), inMenu);
13993            addInfoToMenu(divergingBString, layoutTurnoutB.getSignalC1Name(), inMenu);
13994        } else if (linkType == LayoutTurnout.LinkType.SECOND_3_WAY) {
13995            JMenuItem jmi = inMenu.add(Bundle.getMessage("ThreeWay"));
13996            jmi.setEnabled(false);
13997            inMenu.add(new JSeparator());
13998            before_mcc += 2;
13999            addInfoToMenu(throatString + " " + continuingString, layoutTurnoutB.getSignalA1Name(), inMenu);
14000            addInfoToMenu(throatString + " " + divergingAString, layoutTurnoutB.getSignalA2Name(), inMenu);
14001            addInfoToMenu(throatString + " " + divergingBString, layoutTurnoutB.getSignalA3Name(), inMenu);
14002            addInfoToMenu(continuingString, layoutTurnoutB.getSignalC1Name(), inMenu);
14003            addInfoToMenu(divergingAString, layoutTurnoutA.getSignalB1Name(), inMenu);
14004            addInfoToMenu(divergingBString, layoutTurnoutA.getSignalC1Name(), inMenu);
14005        }
14006        int after_mcc = inMenu.getMenuComponentCount();
14007        if (before_mcc != after_mcc) {
14008            inMenu.add(new JSeparator());
14009            result = true;   //it's GOOD!
14010        }
14011        return result;
14012    }   //addLayoutTurnoutSignalHeadInfoToMenu
14013
14014    protected Boolean addBlockBoundarySignalHeadInfoToMenu(
14015            @Nonnull PositionablePoint inPositionablePoint,
14016            @Nonnull JMenu inMenu) {
14017        Boolean result = false; //assume failure (pessimist!)
14018
14019        int before_mcc = inMenu.getMenuComponentCount();
14020        if (before_mcc != 0) {
14021            inMenu.add(new JSeparator());
14022        }
14023
14024        JMenuItem jmi = inMenu.add(Bundle.getMessage("BlockBoundary"));
14025        jmi.setEnabled(false);
14026        inMenu.add(new JSeparator());
14027        before_mcc += 2;
14028
14029        addInfoToMenu(Bundle.getMessage("East/SouthBound"), inPositionablePoint.getEastBoundSignal(), inMenu);
14030        addInfoToMenu(Bundle.getMessage("West/NorthBound"), inPositionablePoint.getWestBoundSignal(), inMenu);
14031
14032        int after_mcc = inMenu.getMenuComponentCount();
14033        if (before_mcc != after_mcc) {
14034            inMenu.add(new JSeparator());
14035            result = true;   //it's GOOD!
14036        }
14037
14038        return result;
14039    }
14040
14041    protected Boolean addLevelXingSignalHeadInfoToMenu(
14042            @Nonnull LevelXing inLevelXing,
14043            @Nonnull JMenu inMenu) {
14044        Boolean result = false; //assume failure (pessimist!)
14045
14046        int before_mcc = inMenu.getMenuComponentCount();
14047        if (before_mcc != 0) {
14048            inMenu.add(new JSeparator());
14049        }
14050
14051        JMenuItem jmi = inMenu.add(Bundle.getMessage("LevelCrossing"));
14052        jmi.setEnabled(false);
14053        inMenu.add(new JSeparator());
14054        before_mcc += 2;
14055
14056        addInfoToMenu(Bundle.getMessage("MakeLabel",
14057                Bundle.getMessage("TrackXConnect", "A")),
14058                inLevelXing.getSignalAName(), inMenu);
14059        addInfoToMenu(Bundle.getMessage("MakeLabel",
14060                Bundle.getMessage("TrackXConnect", "B")),
14061                inLevelXing.getSignalBName(), inMenu);
14062        addInfoToMenu(Bundle.getMessage("MakeLabel",
14063                Bundle.getMessage("TrackXConnect", "C")),
14064                inLevelXing.getSignalCName(), inMenu);
14065        addInfoToMenu(Bundle.getMessage("MakeLabel",
14066                Bundle.getMessage("TrackXConnect", "D")),
14067                inLevelXing.getSignalDName(), inMenu);
14068
14069        int after_mcc = inMenu.getMenuComponentCount();
14070        if (before_mcc != after_mcc) {
14071            inMenu.add(new JSeparator());
14072            result = true;   //it's GOOD!
14073        }
14074
14075        return result;
14076    }
14077
14078    protected Boolean addLayoutSlipSignalHeadInfoToMenu(
14079            @Nonnull LayoutTurnout inLayoutTurnout,
14080            @Nonnull JMenu inMenu) {
14081        Boolean result = false; //assume failure (pessimist!)
14082
14083        int before_mcc = inMenu.getMenuComponentCount();
14084        if (before_mcc != 0) {
14085            inMenu.add(new JSeparator());
14086        }
14087
14088        JMenuItem jmi = inMenu.add(Bundle.getMessage("Slip"));
14089        jmi.setEnabled(false);
14090        inMenu.add(new JSeparator());
14091        before_mcc += 2;
14092
14093        addInfoToMenu("A " + continuingString, inLayoutTurnout.getSignalA1Name(), inMenu);
14094        addInfoToMenu("A " + divergingString, inLayoutTurnout.getSignalA2Name(), inMenu);
14095        addInfoToMenu("B " + continuingString, inLayoutTurnout.getSignalB1Name(), inMenu);
14096        addInfoToMenu("B " + divergingString, inLayoutTurnout.getSignalB2Name(), inMenu);
14097        addInfoToMenu("C " + continuingString, inLayoutTurnout.getSignalC1Name(), inMenu);
14098        addInfoToMenu("C " + divergingString, inLayoutTurnout.getSignalC2Name(), inMenu);
14099        addInfoToMenu("D " + continuingString, inLayoutTurnout.getSignalD1Name(), inMenu);
14100        addInfoToMenu("D " + divergingString, inLayoutTurnout.getSignalD2Name(), inMenu);
14101
14102        int after_mcc = inMenu.getMenuComponentCount();
14103        if (before_mcc != after_mcc) {
14104            inMenu.add(new JSeparator());
14105            result = true;   //it's GOOD!
14106        }
14107
14108        return result;
14109    }
14110
14111    private void addInfoToMenu(@CheckForNull String title,
14112            @CheckForNull String info, @Nonnull JMenu menu) {
14113        if ((title != null) && !title.isEmpty() && (info != null) && !info.isEmpty()) {
14114            addInfoToMenu(title + ": " + info, menu);
14115        }
14116    }
14117
14118    private void addInfoToMenu(@CheckForNull String info, @Nonnull JMenu menu) {
14119        if ((info != null) && !info.isEmpty()) {
14120            JMenuItem jmi = new JMenuItem(info);
14121            jmi.setEnabled(false);
14122            menu.add(jmi);
14123        }
14124    }
14125
14126    private void oneFrameToRuleThemAll(@Nonnull JmriJFrame goodFrame) {
14127        setSensorsAtBlockBoundaryFrame = closeIfNotFrame(goodFrame, setSensorsAtBlockBoundaryFrame);
14128        setSensorsAtLevelXingFrame = closeIfNotFrame(goodFrame, setSensorsAtLevelXingFrame);
14129        setSensorsAtSlipFrame = closeIfNotFrame(goodFrame, setSensorsAtSlipFrame);
14130        setSensorsAtTurnoutFrame = closeIfNotFrame(goodFrame, setSensorsAtTurnoutFrame);
14131        setSignalMastsAtBlockBoundaryFrame = closeIfNotFrame(goodFrame, setSignalMastsAtBlockBoundaryFrame);
14132        setSignalMastsAtLayoutSlipFrame = closeIfNotFrame(goodFrame, setSignalMastsAtLayoutSlipFrame);
14133        setSignalMastsAtLevelXingFrame = closeIfNotFrame(goodFrame, setSignalMastsAtLevelXingFrame);
14134        setSignalMastsAtTurnoutFrame = closeIfNotFrame(goodFrame, setSignalMastsAtTurnoutFrame);
14135        setSignalsAt3WayTurnoutFrame = closeIfNotFrame(goodFrame, setSignalsAt3WayTurnoutFrame);
14136        setSignalsAtBlockBoundaryFrame = closeIfNotFrame(goodFrame, setSignalsAtBlockBoundaryFrame);
14137        setSignalsAtLevelXingFrame = closeIfNotFrame(goodFrame, setSignalsAtLevelXingFrame);
14138        setSignalsAtSlipFrame = closeIfNotFrame(goodFrame, setSignalsAtSlipFrame);
14139        setSignalsAtThroatToThroatTurnoutsFrame = closeIfNotFrame(goodFrame, setSignalsAtThroatToThroatTurnoutsFrame);
14140        setSignalsAtTurnoutFrame = closeIfNotFrame(goodFrame, setSignalsAtTurnoutFrame);
14141        setSignalsAtXoverTurnoutFrame = closeIfNotFrame(goodFrame, setSignalsAtXoverTurnoutFrame);
14142    }
14143
14144    private JmriJFrame closeIfNotFrame(@Nonnull JmriJFrame goodFrame, @CheckForNull JmriJFrame badFrame) {
14145        JmriJFrame result = badFrame;
14146        if ((badFrame != null) && (goodFrame != badFrame)) {
14147            badFrame.setVisible(false);
14148            badFrame.dispose();
14149            result = null;
14150        }
14151        return result;
14152    }
14153
14154    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutEditorTools.class);
14155}