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 */
043final public 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    }   //createListUsedSignalMasts
9698
9699    void refreshSignalMastAtTurnoutComboBox() {
9700        turnoutSignalMastsGetSaved(null);
9701        createListUsedSignalMasts();
9702
9703        usedMasts.remove(turnoutSignalMastA.getBean());
9704        usedMasts.remove(turnoutSignalMastB.getBean());
9705        usedMasts.remove(turnoutSignalMastC.getBean());
9706        usedMasts.remove(turnoutSignalMastD.getBean());
9707
9708        turnoutSignalMastA.getCombo().setExcludedItems(usedMasts);
9709        turnoutSignalMastB.getCombo().setExcludedItems(usedMasts);
9710        turnoutSignalMastC.getCombo().setExcludedItems(usedMasts);
9711        turnoutSignalMastD.getCombo().setExcludedItems(usedMasts);
9712    }
9713
9714    private LayoutTurnout.Geometry isMastAssignedHere(
9715            @CheckForNull SignalMast mast,
9716            @CheckForNull LayoutTurnout lTurnout) {
9717        if ((mast == null) || (lTurnout == null)) {
9718            return LayoutTurnout.Geometry.NONE;
9719        }
9720        String sysName = mast.getSystemName();
9721        String uName = mast.getUserName();
9722
9723        String name = lTurnout.getSignalAMastName();
9724        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9725            return LayoutTurnout.Geometry.POINTA1;
9726        }
9727        name = lTurnout.getSignalBMastName();
9728        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9729            return LayoutTurnout.Geometry.POINTA2;
9730        }
9731        name = lTurnout.getSignalCMastName();
9732        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9733            return LayoutTurnout.Geometry.POINTA3;
9734        }
9735        name = lTurnout.getSignalDMastName();
9736        if (!name.isEmpty() && (name.equals(uName) || name.equals(sysName))) {
9737            return LayoutTurnout.Geometry.POINTB1;
9738        }
9739        return LayoutTurnout.Geometry.NONE;
9740    }   //isMastAssignedHere
9741
9742    public void removeAssignment(@Nonnull SignalMast mast) {
9743        String sName = mast.getSystemName();
9744        String uName = mast.getUserName();
9745        for (LayoutTurnout to : layoutEditor.getLayoutTurnouts()) {
9746            if ((to.getSignalAMastName().equals(sName) || ((uName != null)
9747                    && (to.getSignalAMastName().equals(uName))))) {
9748                to.setSignalAMast("");
9749            }
9750            if ((to.getSignalBMastName().equals(sName) || ((uName != null)
9751                    && (to.getSignalBMastName().equals(uName))))) {
9752                to.setSignalBMast("");
9753            }
9754            if ((to.getSignalCMastName().equals(sName) || ((uName != null)
9755                    && (to.getSignalCMastName().equals(uName))))) {
9756                to.setSignalCMast("");
9757            }
9758            if ((to.getSignalDMastName().equals(sName) || ((uName != null)
9759                    && (to.getSignalDMastName().equals(uName))))) {
9760                to.setSignalDMast("");
9761            }
9762        }
9763        for (PositionablePoint po : layoutEditor.getPositionablePoints()) {
9764            if (po.getEastBoundSignalMastName().equals(sName) || po.getEastBoundSignalMastName().equals(uName)) {
9765                po.setEastBoundSignalMast("");
9766            }
9767            if (po.getWestBoundSignalMastName().equals(sName) || po.getWestBoundSignalMastName().equals(uName)) {
9768                po.setWestBoundSignalMast("");
9769            }
9770        }
9771        for (LevelXing x : layoutEditor.getLevelXings()) {
9772            if ( (x.getSignalAMastName().equals(sName) || ((uName != null)
9773                    && (x.getSignalAMastName().equals(uName))))) {
9774                x.setSignalAMast("");
9775            }
9776            if ( (x.getSignalBMastName().equals(sName) || ((uName != null)
9777                    && (x.getSignalBMastName().equals(uName))))) {
9778                x.setSignalBMast("");
9779            }
9780            if ( (x.getSignalCMastName().equals(sName) || ((uName != null)
9781                    && (x.getSignalCMastName().equals(uName))))) {
9782                x.setSignalCMast("");
9783            }
9784            if ( (x.getSignalDMastName().equals(sName) || ((uName != null)
9785                    && (x.getSignalDMastName().equals(uName))))) {
9786                x.setSignalDMast("");
9787            }
9788        }
9789    }   //removeAssignment
9790
9791    private boolean getTurnoutMastInformation() {
9792        turnout = null;
9793        layoutTurnout = null;
9794        String str = signalMastsTurnoutComboBox.getSelectedItemDisplayName();
9795        if ((str == null) || str.isEmpty()) {
9796            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame, Bundle.getMessage("SignalsError1") + "qqq",
9797                    Bundle.getMessage("ErrorTitle"),
9798                    JmriJOptionPane.ERROR_MESSAGE);
9799            return false;
9800        }
9801        turnout = InstanceManager.turnoutManagerInstance().getTurnout(str);
9802        if (turnout == null) {
9803            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9804                    Bundle.getMessage("SignalsError2",
9805                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
9806                    JmriJOptionPane.ERROR_MESSAGE);
9807            return false;
9808        } else {
9809            String uname = turnout.getUserName();
9810            if ((uname == null) || uname.isEmpty()
9811                    || !uname.equals(str)) {
9812                signalMastsTurnoutComboBox.setSelectedItem(turnout);
9813            }
9814        }
9815        layoutTurnout = layoutEditor.getFinder().findLayoutTurnoutByBean(turnout);
9816
9817        if (layoutTurnout == null) {
9818            JmriJOptionPane.showMessageDialog(setSignalsAtTurnoutFrame,
9819                    Bundle.getMessage("SignalsError3",
9820                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
9821                    JmriJOptionPane.ERROR_MESSAGE);
9822            return false;
9823        }
9824        return true;
9825    }
9826
9827    public void placingBlock(PositionableIcon icon, boolean isRightSide,
9828            double fromPoint, Object obj, Point2D p) {
9829        if (obj instanceof TrackSegment) {
9830            TrackSegment ts = (TrackSegment) obj;
9831            Point2D endPoint;
9832            if (ts.getConnect1() == layoutTurnout) {
9833                endPoint = layoutEditor.getCoords(ts.getConnect2(), ts.getType2());
9834            } else {
9835                endPoint = layoutEditor.getCoords(ts.getConnect1(), ts.getType1());
9836            }
9837            boolean isEast = false;
9838            if (MathUtil.equals(endPoint.getX(), p.getX())) {
9839                log.debug("X in both is the same");
9840                if (endPoint.getY() < p.getY()) {
9841                    log.debug("Y end point is less than our point");
9842                    isEast = true;
9843                }
9844            } else if (endPoint.getX() < p.getX()) {
9845                log.debug("end X point is less than our point");
9846                isEast = true;
9847            }
9848
9849            log.debug("East set is {}", isEast);
9850            setIconOnPanel(ts, icon, isEast, p, endPoint, isRightSide, fromPoint);
9851        }
9852    }
9853
9854    public void placingBlockForTurntable(PositionableIcon icon, boolean isRightSide,
9855                             double fromPoint, Object obj, Point2D p) {
9856        if (obj instanceof TrackSegment) {
9857            TrackSegment ts = (TrackSegment) obj;
9858            Point2D endPoint;
9859            // Determine the "other" end of the track segment by comparing coordinates
9860            // with the passed-in anchor point 'p'. This is more robust than checking object references.
9861            if (MathUtil.distance(layoutEditor.getCoords(ts.getConnect1(), ts.getType1()), p) < 1.0) {
9862                // connect1 is at our anchor point 'p', so the other end is connect2
9863                endPoint = layoutEditor.getCoords(ts.getConnect2(), ts.getType2());
9864            } else {
9865                // connect2 is at our anchor point 'p', so the other end is connect1
9866                endPoint = layoutEditor.getCoords(ts.getConnect1(), ts.getType1());
9867            }
9868            boolean isEast = false;
9869            if (MathUtil.equals(endPoint.getX(), p.getX())) {
9870                log.debug("X in both is the same");
9871                if (endPoint.getY() < p.getY()) {
9872                    log.debug("Y end point is less than our point");
9873                    isEast = true;
9874                }
9875            } else if (endPoint.getX() < p.getX()) {
9876                log.debug("end X point is less than our point");
9877                isEast = true;
9878            }
9879
9880            log.debug("East set is {}", isEast);
9881            setIconOnPanel(ts, icon, isEast, p, endPoint, isRightSide, fromPoint);
9882        }
9883    }
9884
9885    private void setSignalMastsCancelPressed(ActionEvent a) {
9886        setSignalMastsAtTurnoutOpenFlag = false;
9887        setSignalMastsAtTurnoutFrame.setVisible(false);
9888    }
9889
9890    /*============================*\
9891    |* setSignalMastsAtLayoutSlip *|
9892    \*============================*/
9893
9894    //operational variables for Set SignalMast at Slip tool
9895    private JmriJFrame setSignalMastsAtLayoutSlipFrame = null;
9896    private boolean setSignalMastsAtLayoutSlipOpenFlag = false;
9897    private boolean setSignalMastsAtLayoutSlipFromMenuFlag = false;
9898
9899    private JButton getSavedSlipSignalMasts = null;
9900    private JButton setSlipSignalMastsDone = null;
9901    private JButton setSlipSignalMastsCancel = null;
9902
9903    private String[] slipBlocks = new String[4];
9904
9905    private final NamedBeanComboBox<Block> slipSignalBlockAComboBox
9906            = new NamedBeanComboBox<>(
9907                    InstanceManager.getDefault(BlockManager.class),
9908                    null, DisplayOptions.DISPLAYNAME);
9909    private final NamedBeanComboBox<Block> slipSignalBlockBComboBox
9910            = new NamedBeanComboBox<>(
9911                    InstanceManager.getDefault(BlockManager.class),
9912                    null, DisplayOptions.DISPLAYNAME);
9913    private final NamedBeanComboBox<Block> slipSignalBlockCComboBox
9914            = new NamedBeanComboBox<>(
9915                    InstanceManager.getDefault(BlockManager.class),
9916                    null, DisplayOptions.DISPLAYNAME);
9917    private final NamedBeanComboBox<Block> slipSignalBlockDComboBox
9918            = new NamedBeanComboBox<>(
9919                    InstanceManager.getDefault(BlockManager.class),
9920                    null, DisplayOptions.DISPLAYNAME);
9921
9922    private JLabel slipSignalBlockANameLabel = null;
9923    private JLabel slipSignalBlockBNameLabel = null;
9924    private JLabel slipSignalBlockCNameLabel = null;
9925    private JLabel slipSignalBlockDNameLabel = null;
9926
9927    BeanDetails<SignalMast> slipSignalMastA;
9928    BeanDetails<SignalMast> slipSignalMastB;
9929    BeanDetails<SignalMast> slipSignalMastC;
9930    BeanDetails<SignalMast> slipSignalMastD;
9931
9932    JPanel signalMastLayoutSlipPanel = new JPanel(new FlowLayout());
9933
9934    public void setSignalMastsAtSlipFromMenu(@Nonnull LayoutSlip slip,
9935            @Nonnull String[] blocks, @Nonnull JFrame theFrame) {
9936        layoutSlip = slip;
9937        layoutTurnout = slip;
9938
9939        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
9940        slipSignalBlockAComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockName()));
9941        slipSignalBlockBComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockBName()));
9942        slipSignalBlockCComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockCName()));
9943        slipSignalBlockDComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockDName()));
9944
9945        slipBlocks = new String[4];
9946        for (int i = 0; i < blocks.length; i++) {
9947            slipBlocks[i] = blocks[i];
9948        }
9949        setSignalMastsAtLayoutSlipFromMenuFlag = true;
9950        setSignalMastsAtLayoutSlip(theFrame);
9951    }
9952
9953    //TODO: Add to Tools menu?
9954    public void setSignalMastsAtLayoutSlip(@Nonnull JFrame theFrame) {
9955        signalFrame = theFrame;
9956
9957        //Initialize if needed
9958        if (setSignalMastsAtLayoutSlipFrame == null) {
9959            setSignalMastsAtLayoutSlipOpenFlag = false;
9960
9961            slipSignalMastA = new BeanDetails<>("SignalMast",
9962                    InstanceManager.getDefault(SignalMastManager.class));
9963            slipSignalMastB = new BeanDetails<>("SignalMast",
9964                    InstanceManager.getDefault(SignalMastManager.class));
9965            slipSignalMastC = new BeanDetails<>("SignalMast",
9966                    InstanceManager.getDefault(SignalMastManager.class));
9967            slipSignalMastD = new BeanDetails<>("SignalMast",
9968                    InstanceManager.getDefault(SignalMastManager.class));
9969
9970            slipSignalMastA.getDetailsPanel().setBackground(new Color(255, 255, 200));
9971            slipSignalMastB.getDetailsPanel().setBackground(new Color(200, 255, 255));
9972            slipSignalMastC.getDetailsPanel().setBackground(new Color(200, 200, 255));
9973            slipSignalMastD.getDetailsPanel().setBackground(new Color(255, 200, 200));
9974
9975            setSignalMastsAtLayoutSlipFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtLayoutSlip"), false, true);
9976            oneFrameToRuleThemAll(setSignalMastsAtLayoutSlipFrame);
9977            setSignalMastsAtLayoutSlipFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
9978//         setSignalMastsAtLayoutSlipFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtLayoutSlip", true);
9979            setSignalMastsAtLayoutSlipFrame.setLocation(70, 30);
9980            Container theContentPane = setSignalMastsAtLayoutSlipFrame.getContentPane();
9981            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
9982
9983            JPanel panel11A = new JPanel(new FlowLayout());
9984//note: this is just placeholder text; real text is set below
9985            slipSignalBlockANameLabel = new JLabel(" A ");
9986            panel11A.add(slipSignalBlockANameLabel);
9987            panel11A.add(slipSignalBlockAComboBox);
9988            slipSignalBlockAComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
9989            theContentPane.add(panel11A);
9990
9991            JPanel panel11B = new JPanel(new FlowLayout());
9992//note: this is just placeholder text; real text is set below
9993            slipSignalBlockBNameLabel = new JLabel(" B ");
9994            panel11B.add(slipSignalBlockBNameLabel);
9995            panel11B.add(slipSignalBlockBComboBox);
9996            slipSignalBlockBComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
9997            theContentPane.add(panel11B);
9998
9999            JPanel panel11C = new JPanel(new FlowLayout());
10000//note: this is just placeholder text; real text is set below
10001            slipSignalBlockCNameLabel = new JLabel(" C ");
10002            panel11C.add(slipSignalBlockCNameLabel);
10003            panel11C.add(slipSignalBlockCComboBox);
10004            slipSignalBlockCComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10005            theContentPane.add(panel11C);
10006
10007            JPanel panel11D = new JPanel(new FlowLayout());
10008//note: this is just placeholder text; real text is set below
10009            slipSignalBlockDNameLabel = new JLabel(" D ");
10010            panel11D.add(slipSignalBlockDNameLabel);
10011            panel11D.add(slipSignalBlockDComboBox);
10012            slipSignalBlockDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10013            theContentPane.add(panel11D);
10014
10015            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10016
10017            JPanel panel2 = new JPanel(new FlowLayout());
10018            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSignalMast"));
10019            panel2.add(shTitle);
10020            panel2.add(new JLabel("   "));
10021            panel2.add(getSavedSlipSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
10022            getSavedSlipSignalMasts.addActionListener(this::slipSignalMastsGetSaved);
10023            getSavedSlipSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
10024            theContentPane.add(panel2);
10025
10026            signalMastLayoutSlipPanel.setLayout(new GridLayout(0, 2));
10027            theContentPane.add(signalMastLayoutSlipPanel);
10028            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10029
10030            JPanel panel6 = new JPanel(new FlowLayout());
10031
10032            panel6.add(new JLabel("   "));
10033            panel6.add(setSlipSignalMastsDone = new JButton(Bundle.getMessage("ButtonDone")));
10034            setSlipSignalMastsDone.addActionListener(this::setSlipSignalMastsDonePressed);
10035            setSlipSignalMastsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
10036            panel6.add(setSlipSignalMastsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
10037            setSlipSignalMastsCancel.addActionListener(this::setSlipSignalMastsCancelPressed);
10038            setSlipSignalMastsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
10039            theContentPane.add(panel6);
10040            setSignalMastsAtLayoutSlipFrame.addWindowListener(new WindowAdapter() {
10041                @Override
10042                public void windowClosing(WindowEvent e) {
10043                    setSlipSignalMastsCancelPressed(null);
10044                }
10045            });
10046        }
10047
10048        //Unhide any excluded masts
10049        slipSignalMastA.getCombo().setExcludedItems(new HashSet<>());
10050        slipSignalMastB.getCombo().setExcludedItems(new HashSet<>());
10051        slipSignalMastC.getCombo().setExcludedItems(new HashSet<>());
10052        slipSignalMastD.getCombo().setExcludedItems(new HashSet<>());
10053        signalMastLayoutSlipPanel.removeAll();
10054
10055        slipSignalBlockAComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10056        slipSignalBlockBComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10057        slipSignalBlockCComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10058        slipSignalBlockDComboBox.setVisible(!setSignalMastsAtLayoutSlipFromMenuFlag);
10059
10060        if (setSignalMastsAtLayoutSlipFromMenuFlag) {
10061            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
10062                    Bundle.getMessage("BeanNameBlock") + " A "
10063                    + Bundle.getMessage("Name"))
10064                    + " " + layoutSlip.getBlockName());
10065            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
10066                    Bundle.getMessage("BeanNameBlock") + " B "
10067                    + Bundle.getMessage("Name"))
10068                    + " " + layoutSlip.getBlockBName());
10069            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
10070                    Bundle.getMessage("BeanNameBlock") + " C "
10071                    + Bundle.getMessage("Name"))
10072                    + " " + layoutSlip.getBlockCName());
10073            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
10074                    Bundle.getMessage("BeanNameBlock") + " D "
10075                    + Bundle.getMessage("Name"))
10076                    + " " + layoutSlip.getBlockDName());
10077            refreshSignalMastAtSlipComboBox();
10078        } else {
10079            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
10080                    Bundle.getMessage("BeanNameBlock") + " A "
10081                    + Bundle.getMessage("Name")));
10082            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
10083                    Bundle.getMessage("BeanNameBlock") + " B "
10084                    + Bundle.getMessage("Name")));
10085            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
10086                    Bundle.getMessage("BeanNameBlock") + " C "
10087                    + Bundle.getMessage("Name")));
10088            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
10089                    Bundle.getMessage("BeanNameBlock") + " D "
10090                    + Bundle.getMessage("Name")));
10091        }
10092
10093        if (!setSignalMastsAtLayoutSlipOpenFlag) {
10094            setSignalMastsAtLayoutSlipFrame.setPreferredSize(null);
10095            setSignalMastsAtLayoutSlipFrame.pack();
10096            setSignalMastsAtLayoutSlipOpenFlag = true;
10097        }
10098        setSignalMastsAtLayoutSlipFrame.setVisible(true);
10099    }
10100
10101    void refreshSignalMastAtSlipComboBox() {
10102        slipSignalMastsGetSaved(null);
10103        createListUsedSignalMasts();
10104
10105        usedMasts.remove(slipSignalMastA.getBean());
10106        usedMasts.remove(slipSignalMastB.getBean());
10107        usedMasts.remove(slipSignalMastC.getBean());
10108        usedMasts.remove(slipSignalMastD.getBean());
10109
10110        slipSignalMastA.getCombo().setExcludedItems(usedMasts);
10111        slipSignalMastB.getCombo().setExcludedItems(usedMasts);
10112        slipSignalMastC.getCombo().setExcludedItems(usedMasts);
10113        slipSignalMastD.getCombo().setExcludedItems(usedMasts);
10114    }
10115
10116    private void slipSignalMastsGetSaved(ActionEvent a) {
10117        if (!getSlipMastInformation()) {
10118            return;
10119        }
10120        slipBlocks = layoutSlip.getBlockBoundaries();
10121
10122        slipSignalMastA.setTextField(layoutSlip.getSignalAMastName());
10123        slipSignalMastB.setTextField(layoutSlip.getSignalBMastName());
10124        slipSignalMastC.setTextField(layoutSlip.getSignalCMastName());
10125        slipSignalMastD.setTextField(layoutSlip.getSignalDMastName());
10126
10127        slipSignalMastA.setBoundaryLabel(slipBlocks[0]);
10128        slipSignalMastB.setBoundaryLabel(slipBlocks[1]);
10129        slipSignalMastC.setBoundaryLabel(slipBlocks[2]);
10130        slipSignalMastD.setBoundaryLabel(slipBlocks[3]);
10131
10132        boolean boundaryFlag = false;
10133        signalMastLayoutSlipPanel.remove(slipSignalMastA.getDetailsPanel());
10134        signalMastLayoutSlipPanel.remove(slipSignalMastB.getDetailsPanel());
10135        signalMastLayoutSlipPanel.remove(slipSignalMastC.getDetailsPanel());
10136        signalMastLayoutSlipPanel.remove(slipSignalMastD.getDetailsPanel());
10137        if (slipBlocks[0] != null) {
10138            signalMastLayoutSlipPanel.add(slipSignalMastA.getDetailsPanel());
10139            boundaryFlag = true;
10140        }
10141        if (slipBlocks[1] != null) {
10142            signalMastLayoutSlipPanel.add(slipSignalMastB.getDetailsPanel());
10143            boundaryFlag = true;
10144        }
10145        if (slipBlocks[2] != null) {
10146            signalMastLayoutSlipPanel.add(slipSignalMastC.getDetailsPanel());
10147            boundaryFlag = true;
10148        }
10149        if (slipBlocks[3] != null) {
10150            signalMastLayoutSlipPanel.add(slipSignalMastD.getDetailsPanel());
10151            boundaryFlag = true;
10152        }
10153        if (!boundaryFlag) {
10154            JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame, "There are no block boundaries on this level crossing\nIt is therefore not possible to add Signal Masts to it");
10155        }
10156        setSignalMastsAtLayoutSlipFrame.setPreferredSize(null);
10157        setSignalMastsAtLayoutSlipFrame.pack();
10158    }
10159
10160    private boolean getSlipMastInformation() {
10161        if (!setSignalMastsAtLayoutSlipFromMenuFlag) {
10162            layoutSlip = null;
10163            List<LayoutSlip> layoutSlips = layoutEditor.getLayoutSlips();
10164            if (layoutSlips.size() <= 0) {
10165                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10166                        Bundle.getMessage("SignalsError15"),
10167                        Bundle.getMessage("ErrorTitle"),
10168                        JmriJOptionPane.ERROR_MESSAGE);
10169                return false;
10170            } else if (layoutSlips.size() == 1) {
10171                layoutSlip = layoutSlips.get(0);
10172            } else {
10173                LayoutBlock slipBlockA = null;
10174                //LayoutBlock slipBlockC = null;
10175                slipBlockA = getBlockFromEntry(xingBlockACComboBox);
10176                if (slipBlockA == null) {
10177                    return false;
10178                }
10179
10180                int foundCount = 0;
10181                //make two block tests first
10182                for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
10183                    LayoutBlock xA = null;
10184                    LayoutBlock xB = null;
10185                    LayoutBlock xC = null;
10186                    LayoutBlock xD = null;
10187
10188                    LayoutBlock xAC = x.getLayoutBlock();
10189                    if (x.getConnectA() != null) {
10190                        xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
10191                    }
10192                    if (x.getConnectB() != null) {
10193                        xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
10194                    }
10195                    if (x.getConnectC() != null) {
10196                        xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
10197                    }
10198                    if (x.getConnectD() != null) {
10199                        xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
10200                    }
10201                    if (((xA != null) && (xC != null) && ((xA == slipBlockA)
10202                            || (xC == slipBlockA)))
10203                            || ((xB != null) && (xD != null) && ((xB == slipBlockA)
10204                            || (xD == slipBlockA)))) {
10205                        layoutSlip = x;
10206                        foundCount++;
10207                    } else if ((xAC != null) && (xAC == slipBlockA)) {
10208                        layoutSlip = x;
10209                        foundCount++;
10210                    }
10211                }
10212                if (foundCount == 0) {
10213                    //try one block test
10214                    for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
10215                        if (slipBlockA == x.getLayoutBlock()) {
10216                            layoutSlip = x;
10217                            foundCount++;
10218                        }
10219                    }
10220                }
10221                if (foundCount > 1) {
10222                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10223                            Bundle.getMessage("SignalsError16",
10224                                    new Object[]{" " + foundCount + " "}),
10225                            Bundle.getMessage("ErrorTitle"),
10226                            JmriJOptionPane.ERROR_MESSAGE);
10227                    return false;
10228                }
10229                if (layoutSlip == null) {
10230                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10231                            Bundle.getMessage("SignalsError17"),
10232                            Bundle.getMessage("ErrorTitle"),
10233                            JmriJOptionPane.ERROR_MESSAGE);
10234                    return false;
10235                }
10236            }
10237        }
10238        return true;
10239    }
10240
10241    private void setSlipSignalMastsCancelPressed(ActionEvent a) {
10242        setSignalMastsAtLayoutSlipOpenFlag = false;
10243        setSignalMastsAtLayoutSlipFrame.setVisible(false);
10244    }
10245
10246    private void setSlipSignalMastsDonePressed(ActionEvent a) {
10247        if (!getSlipMastInformation()) {
10248            return;
10249        }
10250
10251        SignalMast aMast = getSignalMastFromEntry(slipSignalMastA.getText(), false, setSignalMastsAtLayoutSlipFrame);
10252        SignalMast bMast = getSignalMastFromEntry(slipSignalMastB.getText(), false, setSignalMastsAtLayoutSlipFrame);
10253        SignalMast cMast = getSignalMastFromEntry(slipSignalMastC.getText(), false, setSignalMastsAtLayoutSlipFrame);
10254        SignalMast dMast = getSignalMastFromEntry(slipSignalMastD.getText(), false, setSignalMastsAtLayoutSlipFrame);
10255
10256        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
10257
10258        //place or update signals as requested
10259        if ((aMast != null) && slipSignalMastA.addToPanel()) {
10260            if (isSignalMastOnPanel(aMast)
10261                    && (aMast != layoutSlip.getSignalAMast())) {
10262                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10263                        Bundle.getMessage("SignalMastsError6",
10264                                new Object[]{slipSignalMastA.getText()}),
10265                        Bundle.getMessage("ErrorTitle"),
10266                        JmriJOptionPane.ERROR_MESSAGE);
10267                return;
10268            } else {
10269                removeSignalMastFromPanel(layoutSlip.getSignalAMast());
10270                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10271                l.setSignalMast(slipSignalMastA.getText());
10272                placingBlock(l, slipSignalMastA.isRightSelected(),
10273                                        0.0, layoutSlip.getConnectA(), layoutSlipView.getCoordsA());
10274                removeAssignment(aMast);
10275                layoutSlip.setSignalAMast(slipSignalMastA.getText());
10276                needRedraw = true;
10277            }
10278        } else if ((aMast != null)
10279                && (aMast != layoutSlip.getSignalAMast())
10280                && (aMast != layoutSlip.getSignalBMast())
10281                && (aMast != layoutSlip.getSignalCMast())
10282                && (aMast != layoutSlip.getSignalDMast())) {
10283            if (isSignalMastOnPanel(aMast)) {
10284                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10285                        Bundle.getMessage("SignalMastsError13",
10286                                new Object[]{slipSignalMastA.getText()}),
10287                        Bundle.getMessage("ErrorTitle"),
10288                        JmriJOptionPane.ERROR_MESSAGE);
10289                return;
10290            } else {
10291                removeSignalMastFromPanel(layoutSlip.getSignalAMast());
10292                removeAssignment(aMast);
10293                layoutSlip.setSignalAMast(slipSignalMastA.getText());
10294            }
10295        } else if ((aMast != null)
10296                && ((aMast == layoutSlip.getSignalBMast())
10297                || (aMast == layoutSlip.getSignalCMast())
10298                || (aMast == layoutSlip.getSignalDMast()))) {
10299            //need to figure out what to do in this case.
10300            log.trace("need to figure out what to do in this case.");
10301        } else if (aMast == null) {
10302            removeSignalMastFromPanel(layoutSlip.getSignalAMast());
10303            layoutSlip.setSignalAMast("");
10304        }
10305        if ((bMast != null) && slipSignalMastB.addToPanel()) {
10306            if (isSignalMastOnPanel(bMast)
10307                    && (bMast != layoutSlip.getSignalBMast())) {
10308                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10309                        Bundle.getMessage("SignalMastsError6",
10310                                new Object[]{slipSignalMastB.getText()}),
10311                        Bundle.getMessage("ErrorTitle"),
10312                        JmriJOptionPane.ERROR_MESSAGE);
10313                return;
10314            } else {
10315                removeSignalMastFromPanel(layoutSlip.getSignalBMast());
10316                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10317                l.setSignalMast(slipSignalMastB.getText());
10318                placingBlock(l, slipSignalMastB.isRightSelected(),
10319                                            0.0, layoutSlip.getConnectB(), layoutSlipView.getCoordsB());
10320                removeAssignment(bMast);
10321                layoutSlip.setSignalBMast(slipSignalMastB.getText());
10322                needRedraw = true;
10323            }
10324        } else if ((bMast != null)
10325                && (bMast != layoutSlip.getSignalAMast())
10326                && (bMast != layoutSlip.getSignalBMast())
10327                && (bMast != layoutSlip.getSignalCMast())
10328                && (bMast != layoutSlip.getSignalDMast())) {
10329            if (isSignalMastOnPanel(bMast)) {
10330                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10331                        Bundle.getMessage("SignalMastsError13",
10332                                new Object[]{slipSignalMastB.getText()}),
10333                        Bundle.getMessage("ErrorTitle"),
10334                        JmriJOptionPane.ERROR_MESSAGE);
10335                return;
10336            } else {
10337                removeSignalMastFromPanel(layoutSlip.getSignalBMast());
10338                removeAssignment(bMast);
10339                layoutSlip.setSignalBMast(slipSignalMastB.getText());
10340            }
10341        } else if ((bMast != null)
10342                && ((bMast == layoutSlip.getSignalAMast())
10343                || (bMast == layoutSlip.getSignalCMast())
10344                || (bMast == layoutSlip.getSignalDMast()))) {
10345            //need to figure out what to do in this case.
10346            log.trace("need to figure out what to do in this case.");
10347        } else if (bMast == null) {
10348            removeSignalMastFromPanel(layoutSlip.getSignalBMast());
10349            layoutSlip.setSignalBMast("");
10350        }
10351        if ((cMast != null) && slipSignalMastC.addToPanel()) {
10352            if (isSignalMastOnPanel(cMast)
10353                    && (cMast != layoutSlip.getSignalCMast())) {
10354                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10355                        Bundle.getMessage("SignalMastsError6",
10356                                new Object[]{slipSignalMastC.getText()}),
10357                        Bundle.getMessage("ErrorTitle"),
10358                        JmriJOptionPane.ERROR_MESSAGE);
10359                return;
10360            } else {
10361                removeSignalMastFromPanel(layoutSlip.getSignalCMast());
10362                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10363                l.setSignalMast(slipSignalMastC.getText());
10364                placingBlock(l, slipSignalMastA.isRightSelected(),
10365                                                0.0, layoutSlip.getConnectC(), layoutSlipView.getCoordsC());
10366                removeAssignment(cMast);
10367                layoutSlip.setSignalCMast(slipSignalMastC.getText());
10368                needRedraw = true;
10369            }
10370        } else if ((cMast != null)
10371                && (cMast != layoutSlip.getSignalAMast())
10372                && (cMast != layoutSlip.getSignalBMast())
10373                && (cMast != layoutSlip.getSignalCMast())
10374                && (cMast != layoutSlip.getSignalDMast())) {
10375            if (isSignalMastOnPanel(cMast)) {
10376                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10377                        Bundle.getMessage("SignalMastsError13",
10378                                new Object[]{slipSignalMastC.getText()}),
10379                        Bundle.getMessage("ErrorTitle"),
10380                        JmriJOptionPane.ERROR_MESSAGE);
10381                return;
10382            } else {
10383                removeSignalMastFromPanel(layoutSlip.getSignalCMast());
10384                removeAssignment(cMast);
10385                layoutSlip.setSignalCMast(slipSignalMastC.getText());
10386            }
10387        } else if ((cMast != null)
10388                && ((cMast == layoutSlip.getSignalBMast())
10389                || (cMast == layoutSlip.getSignalAMast())
10390                || (cMast == layoutSlip.getSignalDMast()))) {
10391            //need to figure out what to do in this case.
10392            log.trace("need to figure out what to do in this case.");
10393        } else if (cMast == null) {
10394            removeSignalMastFromPanel(layoutSlip.getSignalCMast());
10395            layoutSlip.setSignalCMast("");
10396        }
10397        if ((dMast != null) && slipSignalMastD.addToPanel()) {
10398            if (isSignalMastOnPanel(dMast)
10399                    && (dMast != layoutSlip.getSignalDMast())) {
10400                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10401                        Bundle.getMessage("SignalMastsError6",
10402                                new Object[]{slipSignalMastD.getText()}),
10403                        Bundle.getMessage("ErrorTitle"),
10404                        JmriJOptionPane.ERROR_MESSAGE);
10405                return;
10406            } else {
10407                removeSignalMastFromPanel(layoutSlip.getSignalDMast());
10408                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10409                l.setSignalMast(slipSignalMastD.getText());
10410                placingBlock(l, slipSignalMastD.isRightSelected(),
10411                                                0.0, layoutSlip.getConnectD(), layoutSlipView.getCoordsD());
10412                removeAssignment(dMast);
10413                layoutSlip.setSignalDMast(slipSignalMastD.getText());
10414                needRedraw = true;
10415            }
10416        } else if ((dMast != null)
10417                && (dMast != layoutSlip.getSignalAMast())
10418                && (dMast != layoutSlip.getSignalBMast())
10419                && (dMast != layoutSlip.getSignalCMast())
10420                && (dMast != layoutSlip.getSignalDMast())) {
10421            if (isSignalMastOnPanel(dMast)) {
10422                JmriJOptionPane.showMessageDialog(setSignalMastsAtLayoutSlipFrame,
10423                        Bundle.getMessage("SignalMastsError13",
10424                                new Object[]{slipSignalMastD.getText()}),
10425                        Bundle.getMessage("ErrorTitle"),
10426                        JmriJOptionPane.ERROR_MESSAGE);
10427                return;
10428            } else {
10429                removeSignalMastFromPanel(layoutSlip.getSignalDMast());
10430                removeAssignment(dMast);
10431                layoutSlip.setSignalDMast(slipSignalMastD.getText());
10432            }
10433        } else if ((dMast != null)
10434                && ((dMast == layoutSlip.getSignalBMast())
10435                || (dMast == layoutSlip.getSignalCMast())
10436                || (dMast == layoutSlip.getSignalAMast()))) {
10437            //need to figure out what to do in this case.
10438            log.trace("need to figure out what to do in this case.");
10439        } else if (dMast == null) {
10440            removeSignalMastFromPanel(layoutSlip.getSignalDMast());
10441            layoutSlip.setSignalDMast("");
10442        }
10443        //setup logic if requested
10444        //finish up
10445        setSignalMastsAtLayoutSlipOpenFlag = false;
10446        setSignalMastsAtLayoutSlipFrame.setVisible(false);
10447        if (needRedraw) {
10448            layoutEditor.redrawPanel();
10449            needRedraw = false;
10450            layoutEditor.setDirty();
10451        }
10452    }
10453
10454    /*===========================*\
10455    |* setSignalMastsAtLevelXing *|
10456    \*===========================*/
10457    //operational variables for Set SignalMast at Level Crossing tool
10458    private JmriJFrame setSignalMastsAtLevelXingFrame = null;
10459    private boolean setSignalMastsAtLevelXingOpenFlag = false;
10460    private boolean setSignalMastsAtLevelXingFromMenuFlag = false;
10461
10462    private JLabel xingSignalBlockACNameLabel = null;
10463    private JLabel xingSignalBlockBDNameLabel = null;
10464
10465    private final NamedBeanComboBox<Block> xingBlockACComboBox = new NamedBeanComboBox<>(
10466            InstanceManager.getDefault(BlockManager.class),
10467            null, DisplayOptions.DISPLAYNAME);
10468    private final NamedBeanComboBox<Block> xingBlockBDComboBox = new NamedBeanComboBox<>(
10469            InstanceManager.getDefault(BlockManager.class),
10470            null, DisplayOptions.DISPLAYNAME);
10471
10472    private JButton getSavedXingSignalMasts = null;
10473    private JButton setXingSignalMastsDone = null;
10474    private JButton setXingSignalMastsCancel = null;
10475
10476    private String[] xingBlocks = new String[4];
10477
10478    BeanDetails<SignalMast> xingSignalMastA;
10479    BeanDetails<SignalMast> xingSignalMastB;
10480    BeanDetails<SignalMast> xingSignalMastC;
10481    BeanDetails<SignalMast> xingSignalMastD;
10482
10483    JPanel signalMastLevelXingPanel = new JPanel(new FlowLayout());
10484
10485    Border blackline = BorderFactory.createLineBorder(Color.black);
10486
10487    //display dialog for Set Signals at Level Crossing tool
10488    public void setSignalMastsAtLevelXingFromMenu(@Nonnull LevelXing xing,
10489            @Nonnull String[] blocks,
10490            @Nonnull JFrame theFrame) {
10491        levelXing = xing;
10492        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
10493        xingBlockACComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameAC()));
10494        xingBlockBDComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameBD()));
10495        xingBlocks = new String[4];
10496        for (int i = 0; i < blocks.length; i++) {
10497            xingBlocks[i] = blocks[i];
10498        }
10499        setSignalMastsAtLevelXingFromMenuFlag = true;
10500        setSignalMastsAtLevelXing(theFrame);
10501        setSignalMastsAtLevelXingFromMenuFlag = false;
10502    }
10503
10504    //TODO: Add to Tools menu?
10505    public void setSignalMastsAtLevelXing(@Nonnull JFrame theFrame) {
10506        signalFrame = theFrame;
10507
10508        //Initialize if needed
10509        if (setSignalMastsAtLevelXingFrame == null) {
10510            setSignalMastsAtLevelXingOpenFlag = false;
10511
10512            xingSignalMastA = new BeanDetails<>("SignalMast",
10513                    InstanceManager.getDefault(SignalMastManager.class
10514                    ));
10515            xingSignalMastB = new BeanDetails<>("SignalMast",
10516                    InstanceManager.getDefault(SignalMastManager.class
10517                    ));
10518            xingSignalMastC = new BeanDetails<>("SignalMast",
10519                    InstanceManager.getDefault(SignalMastManager.class
10520                    ));
10521            xingSignalMastD = new BeanDetails<>("SignalMast",
10522                    InstanceManager.getDefault(SignalMastManager.class
10523                    ));
10524
10525            xingSignalMastA.getDetailsPanel().setBackground(new Color(255, 255, 200));
10526            xingSignalMastB.getDetailsPanel().setBackground(new Color(200, 255, 255));
10527            xingSignalMastC.getDetailsPanel().setBackground(new Color(200, 200, 255));
10528            xingSignalMastD.getDetailsPanel().setBackground(new Color(255, 200, 200));
10529
10530            setSignalMastsAtLevelXingFrame = new JmriJFrame(Bundle.getMessage("SignalMastsAtLevelXing"), false, true);
10531            oneFrameToRuleThemAll(setSignalMastsAtLevelXingFrame);
10532            setSignalMastsAtLevelXingFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
10533            setSignalMastsAtLevelXingFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtLevelXing", true);
10534            setSignalMastsAtLevelXingFrame.setLocation(70, 30);
10535            Container theContentPane = setSignalMastsAtLevelXingFrame.getContentPane();
10536            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
10537
10538            JPanel panel11 = new JPanel(new FlowLayout());
10539            xingSignalBlockACNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
10540                    Bundle.getMessage("BeanNameBlock") + " AC "
10541                    + Bundle.getMessage("Name")));
10542            panel11.add(xingSignalBlockACNameLabel);
10543            panel11.add(xingBlockACComboBox);
10544            xingBlockACComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10545            theContentPane.add(panel11);
10546
10547            JPanel panel12 = new JPanel(new FlowLayout());
10548            xingSignalBlockBDNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
10549                    Bundle.getMessage("BeanNameBlock") + " BD "
10550                    + Bundle.getMessage("Name")));
10551            panel12.add(xingSignalBlockBDNameLabel);
10552            panel12.add(xingBlockBDComboBox);
10553            xingBlockBDComboBox.setToolTipText(Bundle.getMessage("SignalsBlockNameHint"));
10554            theContentPane.add(panel12);
10555            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10556
10557            JPanel panel2 = new JPanel(new FlowLayout());
10558            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSignalMast"));
10559            panel2.add(shTitle);
10560            panel2.add(new JLabel("   "));
10561            panel2.add(getSavedXingSignalMasts = new JButton(Bundle.getMessage("GetSaved")));
10562            getSavedXingSignalMasts.addActionListener(this::xingSignalMastsGetSaved);
10563            getSavedXingSignalMasts.setToolTipText(Bundle.getMessage("GetSavedHint"));
10564            theContentPane.add(panel2);
10565
10566            signalMastLevelXingPanel.setLayout(new GridLayout(0, 2));
10567
10568            theContentPane.add(signalMastLevelXingPanel);
10569            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
10570
10571            JPanel panel6 = new JPanel(new FlowLayout());
10572
10573            panel6.add(new JLabel("   "));
10574            panel6.add(setXingSignalMastsDone = new JButton(Bundle.getMessage("ButtonDone")));
10575            setXingSignalMastsDone.addActionListener(this::setXingSignalMastsDonePressed);
10576            setXingSignalMastsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
10577            panel6.add(setXingSignalMastsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
10578            setXingSignalMastsCancel.addActionListener(this::setXingSignalMastsCancelPressed);
10579            setXingSignalMastsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
10580            theContentPane.add(panel6);
10581            setSignalMastsAtLevelXingFrame.addWindowListener(new WindowAdapter() {
10582                @Override
10583                public void windowClosing(WindowEvent e) {
10584                    setXingSignalMastsCancelPressed(null);
10585                }
10586            });
10587        } //if (setSignalMastsAtLevelXingFrame == null)
10588
10589        //Unhide any excluded masts
10590        xingSignalMastA.getCombo().setExcludedItems(new HashSet<>());
10591        xingSignalMastB.getCombo().setExcludedItems(new HashSet<>());
10592        xingSignalMastC.getCombo().setExcludedItems(new HashSet<>());
10593        xingSignalMastD.getCombo().setExcludedItems(new HashSet<>());
10594        signalMastLevelXingPanel.removeAll();
10595
10596        if (setSignalMastsAtLevelXingFromMenuFlag) {
10597            xingBlockACComboBox.setVisible(false);
10598            xingBlockBDComboBox.setVisible(false);
10599
10600            xingSignalBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
10601                    (Bundle.getMessage("BeanNameBlock") + " AC"))
10602                    + " " + levelXing.getBlockNameAC());
10603            xingSignalBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
10604                    (Bundle.getMessage("BeanNameBlock") + " BD"))
10605                    + " " + levelXing.getBlockNameBD());
10606
10607            xingSignalMastA.setTextField(levelXing.getSignalAMastName());
10608            xingSignalMastB.setTextField(levelXing.getSignalBMastName());
10609            xingSignalMastC.setTextField(levelXing.getSignalCMastName());
10610            xingSignalMastD.setTextField(levelXing.getSignalDMastName());
10611
10612            xingSignalMastsGetSaved(null);
10613            refreshSignalMastAtXingComboBox();
10614        } else {
10615            xingSignalBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
10616                    Bundle.getMessage("BeanNameBlock") + " AC "
10617                    + Bundle.getMessage("Name")));
10618            xingSignalBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
10619                    Bundle.getMessage("BeanNameBlock") + " BD "
10620                    + Bundle.getMessage("Name")));
10621        }
10622
10623        if (!setSignalMastsAtLevelXingOpenFlag) {
10624            setSignalMastsAtLevelXingFrame.setPreferredSize(null);
10625            setSignalMastsAtLevelXingFrame.pack();
10626            setSignalMastsAtLevelXingOpenFlag = true;
10627        }
10628        setSignalMastsAtLevelXingFrame.setVisible(true);
10629    }   //setSignalMastsAtLevelXing
10630
10631    void refreshSignalMastAtXingComboBox() {
10632        xingSignalMastsGetSaved(null);
10633        createListUsedSignalMasts();
10634
10635        usedMasts.remove(xingSignalMastA.getBean());
10636        usedMasts.remove(xingSignalMastB.getBean());
10637        usedMasts.remove(xingSignalMastC.getBean());
10638        usedMasts.remove(xingSignalMastD.getBean());
10639
10640        xingSignalMastA.getCombo().setExcludedItems(usedMasts);
10641        xingSignalMastB.getCombo().setExcludedItems(usedMasts);
10642        xingSignalMastC.getCombo().setExcludedItems(usedMasts);
10643        xingSignalMastD.getCombo().setExcludedItems(usedMasts);
10644    }
10645
10646    private void xingSignalMastsGetSaved(ActionEvent a) {
10647        if (!getLevelCrossingMastInformation()) {
10648            return;
10649        }
10650        xingBlocks = levelXing.getBlockBoundaries();
10651
10652        xingSignalMastA.setTextField(levelXing.getSignalAMastName());
10653        xingSignalMastB.setTextField(levelXing.getSignalBMastName());
10654        xingSignalMastC.setTextField(levelXing.getSignalCMastName());
10655        xingSignalMastD.setTextField(levelXing.getSignalDMastName());
10656
10657        xingSignalMastA.setBoundaryLabel(xingBlocks[0]);
10658        xingSignalMastB.setBoundaryLabel(xingBlocks[1]);
10659        xingSignalMastC.setBoundaryLabel(xingBlocks[2]);
10660        xingSignalMastD.setBoundaryLabel(xingBlocks[3]);
10661
10662        boolean boundaryFlag = false;
10663        signalMastLevelXingPanel.remove(xingSignalMastA.getDetailsPanel());
10664        signalMastLevelXingPanel.remove(xingSignalMastB.getDetailsPanel());
10665        signalMastLevelXingPanel.remove(xingSignalMastC.getDetailsPanel());
10666        signalMastLevelXingPanel.remove(xingSignalMastD.getDetailsPanel());
10667        if (xingBlocks[0] != null) {
10668            signalMastLevelXingPanel.add(xingSignalMastA.getDetailsPanel());
10669            boundaryFlag = true;
10670        }
10671        if (xingBlocks[1] != null) {
10672            signalMastLevelXingPanel.add(xingSignalMastB.getDetailsPanel());
10673            boundaryFlag = true;
10674        }
10675        if (xingBlocks[2] != null) {
10676            signalMastLevelXingPanel.add(xingSignalMastC.getDetailsPanel());
10677            boundaryFlag = true;
10678        }
10679        if (xingBlocks[3] != null) {
10680            signalMastLevelXingPanel.add(xingSignalMastD.getDetailsPanel());
10681            boundaryFlag = true;
10682        }
10683        if (!boundaryFlag) {
10684            JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame, "There are no block boundaries on this level crossing\nIt is therefore not possible to add Signal Masts to it");
10685        }
10686        setSignalMastsAtLevelXingFrame.setPreferredSize(null);
10687        setSignalMastsAtLevelXingFrame.pack();
10688    }   //xingSignalMastsGetSaved
10689
10690    private boolean getLevelCrossingMastInformation() {
10691        if (!setSignalMastsAtLevelXingFromMenuFlag) {
10692            levelXing = null;
10693            List<LevelXing> levelXings = layoutEditor.getLevelXings();
10694            if (levelXings.size() <= 0) {
10695                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10696                        Bundle.getMessage("SignalsError15"),
10697                        Bundle.getMessage("ErrorTitle"),
10698                        JmriJOptionPane.ERROR_MESSAGE);
10699                return false;
10700            } else if (levelXings.size() == 1) {
10701                levelXing = levelXings.get(0);
10702            } else {
10703                LayoutBlock xingBlockA = null;
10704                LayoutBlock xingBlockC = null;
10705                xingBlockA = getBlockFromEntry(xingBlockACComboBox);
10706                if (xingBlockA == null) {
10707                    return false;
10708                }
10709
10710                String theBlockName = xingBlockBDComboBox.getSelectedItemDisplayName();
10711                if ((theBlockName != null) && !theBlockName.isEmpty()) {
10712                    xingBlockC = getBlockFromEntry(xingBlockBDComboBox);
10713                    if (xingBlockC == null) {
10714                        return false;
10715                    }
10716                }
10717
10718                int foundCount = 0;
10719                //make two block tests first
10720                if (xingBlockC != null) {
10721                    for (LevelXing x : layoutEditor.getLevelXings()) {
10722                        LayoutBlock xA = null;
10723                        LayoutBlock xB = null;
10724                        LayoutBlock xC = null;
10725                        LayoutBlock xD = null;
10726                        LayoutBlock xAC = x.getLayoutBlockAC();
10727                        LayoutBlock xBD = x.getLayoutBlockBD();
10728                        if (x.getConnectA() != null) {
10729                            xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
10730                        }
10731                        if (x.getConnectB() != null) {
10732                            xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
10733                        }
10734                        if (x.getConnectC() != null) {
10735                            xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
10736                        }
10737                        if (x.getConnectD() != null) {
10738                            xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
10739                        }
10740                        if (((xA != null) && (xC != null) && (((xA == xingBlockA) && (xC == xingBlockC))
10741                                || ((xA == xingBlockC) && (xC == xingBlockA))))
10742                                || ((xB != null) && (xD != null) && (((xB == xingBlockA) && (xD == xingBlockC))
10743                                || ((xB == xingBlockC) && (xD == xingBlockA))))) {
10744                            levelXing = x;
10745                            foundCount++;
10746                        } else if ((xAC != null) && (xBD != null) && (((xAC == xingBlockA) && (xBD == xingBlockC))
10747                                || ((xAC == xingBlockC) && (xBD == xingBlockA)))) {
10748                            levelXing = x;
10749                            foundCount++;
10750                        }
10751                    }
10752                }
10753                if (foundCount == 0) {
10754                    //try one block test
10755                    for (LevelXing x : layoutEditor.getLevelXings()) {
10756                        if ((xingBlockA == x.getLayoutBlockAC()) || (xingBlockA == x.getLayoutBlockBD())) {
10757                            levelXing = x;
10758                            foundCount++;
10759                        }
10760                    }
10761                }
10762                if (foundCount > 1) {
10763                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10764                            Bundle.getMessage("SignalsError16",
10765                                    new Object[]{" " + foundCount + " "}),
10766                            Bundle.getMessage("ErrorTitle"),
10767                            JmriJOptionPane.ERROR_MESSAGE);
10768                    return false;
10769                }
10770                if (levelXing == null) {
10771                    JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10772                            Bundle.getMessage("SignalsError17"),
10773                            Bundle.getMessage("ErrorTitle"),
10774                            JmriJOptionPane.ERROR_MESSAGE);
10775                    return false;
10776                }
10777            }
10778        }
10779        return true;
10780    }   //getLevelCrossingMastInformation
10781
10782    private void setXingSignalMastsCancelPressed(ActionEvent a) {
10783        setSignalMastsAtLevelXingOpenFlag = false;
10784        setSignalMastsAtLevelXingFrame.setVisible(false);
10785    }
10786
10787    private void setXingSignalMastsDonePressed(ActionEvent a) {
10788        if (!getLevelCrossingMastInformation()) {
10789            return;
10790        }
10791        SignalMast aMast = getSignalMastFromEntry(xingSignalMastA.getText(), false, setSignalMastsAtLevelXingFrame);
10792        SignalMast bMast = getSignalMastFromEntry(xingSignalMastB.getText(), false, setSignalMastsAtLevelXingFrame);
10793        SignalMast cMast = getSignalMastFromEntry(xingSignalMastC.getText(), false, setSignalMastsAtLevelXingFrame);
10794        SignalMast dMast = getSignalMastFromEntry(xingSignalMastD.getText(), false, setSignalMastsAtLevelXingFrame);
10795
10796        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
10797
10798        //if ( !getXingSignalMastInformation() ) return;
10799        //place or update signals as requested
10800        if ((aMast != null) && xingSignalMastA.addToPanel()) {
10801            if (isSignalMastOnPanel(aMast)
10802                    && (aMast != levelXing.getSignalAMast())) {
10803                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10804                        Bundle.getMessage("SignalMastsError6",
10805                                new Object[]{xingSignalMastA.getText()}),
10806                        Bundle.getMessage("ErrorTitle"),
10807                        JmriJOptionPane.ERROR_MESSAGE);
10808                return;
10809            } else {
10810                removeSignalMastFromPanel(levelXing.getSignalAMast());
10811                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10812                l.setSignalMast(xingSignalMastA.getText());
10813                placingBlock(l, xingSignalMastA.isRightSelected(), 0.0, levelXing.getConnectA(), levelXingView.getCoordsA());
10814                removeAssignment(aMast);
10815                levelXing.setSignalAMast(xingSignalMastA.getText());
10816                needRedraw = true;
10817            }
10818        } else if ((aMast != null)
10819                && (aMast != levelXing.getSignalAMast())
10820                && (aMast != levelXing.getSignalBMast())
10821                && (aMast != levelXing.getSignalCMast())
10822                && (aMast != levelXing.getSignalDMast())) {
10823            if (isSignalMastOnPanel(aMast)) {
10824                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10825                        Bundle.getMessage("SignalMastsError13",
10826                                new Object[]{xingSignalMastA.getText()}),
10827                        Bundle.getMessage("ErrorTitle"),
10828                        JmriJOptionPane.ERROR_MESSAGE);
10829                return;
10830            } else {
10831                removeSignalMastFromPanel(levelXing.getSignalAMast());
10832                removeAssignment(aMast);
10833                levelXing.setSignalAMast(xingSignalMastA.getText());
10834            }
10835        } else if ((aMast != null)
10836                && ((aMast == levelXing.getSignalBMast())
10837                || (aMast == levelXing.getSignalCMast())
10838                || (aMast == levelXing.getSignalDMast()))) {
10839            //need to figure out what to do in this case.
10840            log.trace("need to figure out what to do in this case.");
10841        } else if (aMast == null) {
10842            removeSignalMastFromPanel(levelXing.getSignalAMast());
10843            levelXing.setSignalAMast("");
10844        }
10845        if ((bMast != null) && xingSignalMastB.addToPanel()) {
10846            if (isSignalMastOnPanel(bMast)
10847                    && (bMast != levelXing.getSignalBMast())) {
10848                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10849                        Bundle.getMessage("SignalMastsError6",
10850                                new Object[]{xingSignalMastB.getText()}),
10851                        Bundle.getMessage("ErrorTitle"),
10852                        JmriJOptionPane.ERROR_MESSAGE);
10853                return;
10854            } else {
10855                removeSignalMastFromPanel(levelXing.getSignalBMast());
10856                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10857                l.setSignalMast(xingSignalMastB.getText());
10858                placingBlock(l, xingSignalMastB.isRightSelected(), 0.0, levelXing.getConnectB(), levelXingView.getCoordsB());
10859                removeAssignment(bMast);
10860                levelXing.setSignalBMast(xingSignalMastB.getText());
10861                needRedraw = true;
10862            }
10863        } else if ((bMast != null)
10864                && (bMast != levelXing.getSignalAMast())
10865                && (bMast != levelXing.getSignalBMast())
10866                && (bMast != levelXing.getSignalCMast())
10867                && (bMast != levelXing.getSignalDMast())) {
10868            if (isSignalMastOnPanel(bMast)) {
10869                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10870                        Bundle.getMessage("SignalMastsError13",
10871                                new Object[]{xingSignalMastB.getText()}),
10872                        Bundle.getMessage("ErrorTitle"),
10873                        JmriJOptionPane.ERROR_MESSAGE);
10874                return;
10875            } else {
10876                removeSignalMastFromPanel(levelXing.getSignalBMast());
10877                removeAssignment(bMast);
10878                levelXing.setSignalBMast(xingSignalMastB.getText());
10879            }
10880        } else if ((bMast != null)
10881                && ((bMast == levelXing.getSignalAMast())
10882                || (bMast == levelXing.getSignalCMast())
10883                || (bMast == levelXing.getSignalBMast())
10884                || (bMast == levelXing.getSignalDMast()))) {
10885            //need to figure out what to do in this case.
10886            log.trace("need to figure out what to do in this case.");
10887        } else if (bMast == null) {
10888            removeSignalMastFromPanel(levelXing.getSignalBMast());
10889            levelXing.setSignalBMast("");
10890        }
10891        if ((cMast != null) && xingSignalMastC.addToPanel()) {
10892            if (isSignalMastOnPanel(cMast)
10893                    && (cMast != levelXing.getSignalCMast())) {
10894                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10895                        Bundle.getMessage("SignalMastsError6",
10896                                new Object[]{xingSignalMastC.getText()}),
10897                        Bundle.getMessage("ErrorTitle"),
10898                        JmriJOptionPane.ERROR_MESSAGE);
10899                return;
10900            } else {
10901                removeSignalMastFromPanel(levelXing.getSignalCMast());
10902                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10903                l.setSignalMast(xingSignalMastC.getText());
10904                placingBlock(l, xingSignalMastC.isRightSelected(), 0.0, levelXing.getConnectC(), levelXingView.getCoordsC());
10905                removeAssignment(cMast);
10906                levelXing.setSignalCMast(xingSignalMastC.getText());
10907                needRedraw = true;
10908            }
10909        } else if ((cMast != null)
10910                && (cMast != levelXing.getSignalAMast())
10911                && (cMast != levelXing.getSignalBMast())
10912                && (cMast != levelXing.getSignalCMast())
10913                && (cMast != levelXing.getSignalDMast())) {
10914            if (isSignalMastOnPanel(cMast)) {
10915                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10916                        Bundle.getMessage("SignalMastsError13",
10917                                new Object[]{xingSignalMastC.getText()}),
10918                        Bundle.getMessage("ErrorTitle"),
10919                        JmriJOptionPane.ERROR_MESSAGE);
10920                return;
10921            } else {
10922                removeSignalMastFromPanel(levelXing.getSignalCMast());
10923                removeAssignment(cMast);
10924                levelXing.setSignalCMast(xingSignalMastC.getText());
10925            }
10926        } else if ((cMast != null)
10927                && ((cMast == levelXing.getSignalBMast())
10928                || (cMast == levelXing.getSignalAMast())
10929                || (cMast == levelXing.getSignalDMast()))) {
10930            //need to figure out what to do in this case.
10931            log.trace("need to figure out what to do in this case.");
10932        } else if (cMast == null) {
10933            removeSignalMastFromPanel(levelXing.getSignalCMast());
10934            levelXing.setSignalCName("");
10935        }
10936        if ((dMast != null) && xingSignalMastD.addToPanel()) {
10937            if (isSignalMastOnPanel(dMast)
10938                    && (dMast != levelXing.getSignalDMast())) {
10939                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10940                        Bundle.getMessage("SignalMastsError6",
10941                                new Object[]{xingSignalMastD.getText()}),
10942                        Bundle.getMessage("ErrorTitle"),
10943                        JmriJOptionPane.ERROR_MESSAGE);
10944                return;
10945            } else {
10946                removeSignalMastFromPanel(levelXing.getSignalDMast());
10947                SignalMastIcon l = new SignalMastIcon(layoutEditor);
10948                l.setSignalMast(xingSignalMastD.getText());
10949                placingBlock(l, xingSignalMastD.isRightSelected(), 0.0, levelXing.getConnectD(), levelXingView.getCoordsD());
10950                removeAssignment(dMast);
10951                levelXing.setSignalDMast(xingSignalMastD.getText());
10952                needRedraw = true;
10953            }
10954        } else if ((dMast != null)
10955                && (dMast != levelXing.getSignalAMast())
10956                && (dMast != levelXing.getSignalBMast())
10957                && (dMast != levelXing.getSignalCMast())
10958                && (dMast != levelXing.getSignalDMast())) {
10959            if (isSignalMastOnPanel(dMast)) {
10960                JmriJOptionPane.showMessageDialog(setSignalMastsAtLevelXingFrame,
10961                        Bundle.getMessage("SignalMastsError13",
10962                                new Object[]{xingSignalMastD.getText()}),
10963                        Bundle.getMessage("ErrorTitle"),
10964                        JmriJOptionPane.ERROR_MESSAGE);
10965                return;
10966            } else {
10967                removeSignalMastFromPanel(levelXing.getSignalDMast());
10968                removeAssignment(dMast);
10969                levelXing.setSignalDMast(xingSignalMastD.getText());
10970            }
10971        } else if ((dMast != null)
10972                && ((dMast == levelXing.getSignalBMast())
10973                || (dMast == levelXing.getSignalCMast())
10974                || (dMast == levelXing.getSignalAMast()))) {
10975            //need to figure out what to do in this case.
10976            log.trace("need to figure out what to do in this case.");
10977        } else if (dMast == null) {
10978            removeSignalMastFromPanel(levelXing.getSignalDMast());
10979            levelXing.setSignalDMast("");
10980        }
10981        //setup logic if requested
10982        //finish up
10983        setSignalMastsAtLevelXingOpenFlag = false;
10984        setSignalMastsAtLevelXingFrame.setVisible(false);
10985        if (needRedraw) {
10986            layoutEditor.redrawPanel();
10987            needRedraw = false;
10988            layoutEditor.setDirty();
10989        }
10990    }   //setXingSignalMastsDonePressed
10991
10992    /*=====================*\
10993    |* setSensorsAtTurnout *|
10994    \*=====================*/
10995    private JmriJFrame setSensorsAtTurnoutFrame = null;
10996    private boolean setSensorsAtTurnoutOpenFlag = false;
10997    private boolean setSensorsAtTurnoutFromMenuFlag = false;
10998
10999    private JFrame turnoutSensorFrame = null;
11000    private JLabel turnoutSensorNameLabel = null;
11001
11002    private final NamedBeanComboBox<Turnout> sensorsTurnoutComboBox
11003            = new NamedBeanComboBox<>(
11004                    InstanceManager.turnoutManagerInstance(),
11005                    null, DisplayOptions.DISPLAYNAME);
11006
11007    private JButton setSensorsDone;
11008    private JButton getSavedSensors;
11009    private JButton setSensorsCancel;
11010    private JButton changeSensorIcon = null;
11011
11012    private String[] turnoutSenBlocks = new String[4];
11013
11014    BeanDetails<Sensor> turnoutSensorA;
11015    BeanDetails<Sensor> turnoutSensorB;
11016    BeanDetails<Sensor> turnoutSensorC;
11017    BeanDetails<Sensor> turnoutSensorD;
11018
11019    JPanel sensorTurnoutPanel = new JPanel(new FlowLayout());
11020
11021    public void setSensorsAtTurnoutFromMenu(@Nonnull LayoutTurnout to,
11022            @Nonnull String[] blocks,
11023            @Nonnull MultiIconEditor theEditor,
11024            @Nonnull JFrame frame) {
11025        sensorIconEditor = theEditor;
11026        layoutTurnout = to;
11027        turnout = to.getTurnout();
11028        sensorsTurnoutComboBox.setSelectedItem(turnout);
11029        turnoutSenBlocks = new String[4];
11030        for (int i = 0; i < blocks.length; i++) {
11031            turnoutSenBlocks[i] = blocks[i];
11032        }
11033        setSensorsAtTurnoutFromMenuFlag = true;
11034        setSensorsAtTurnout(frame);
11035        setSensorsAtTurnoutFromMenuFlag = false;
11036    }
11037
11038    //TODO: Add to Tools menu?
11039    public void setSensorsAtTurnout(@Nonnull JFrame frame) {
11040        turnoutSensorFrame = frame;
11041
11042        //Initialize if needed
11043        if (setSensorsAtTurnoutFrame == null) {
11044            setSensorsAtTurnoutOpenFlag = false;
11045
11046            turnoutSensorA = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11047            turnoutSensorB = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11048            turnoutSensorC = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11049            turnoutSensorD = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());  // NOI18N
11050
11051            turnoutSensorA.getDetailsPanel().setBackground(new Color(255, 255, 200));
11052            turnoutSensorB.getDetailsPanel().setBackground(new Color(200, 255, 255));
11053            turnoutSensorC.getDetailsPanel().setBackground(new Color(200, 200, 255));
11054            turnoutSensorD.getDetailsPanel().setBackground(new Color(255, 200, 200));
11055
11056            setSensorsAtTurnoutFrame = new JmriJFrame(Bundle.getMessage("SensorsAtTurnout"), false, true);
11057            oneFrameToRuleThemAll(setSensorsAtTurnoutFrame);
11058            setSensorsAtTurnoutFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
11059//         setSensorsAtTurnoutFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtTurnout", true);
11060            setSensorsAtTurnoutFrame.setLocation(70, 30);
11061            Container theContentPane = setSensorsAtTurnoutFrame.getContentPane();
11062            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
11063
11064            JPanel panel1 = new JPanel(new FlowLayout());
11065
11066            turnoutSensorNameLabel = new JLabel(Bundle.getMessage("BeanNameTurnout") + " "
11067                    + Bundle.getMessage("Name"));
11068            panel1.add(turnoutSensorNameLabel);
11069            panel1.add(sensorsTurnoutComboBox);
11070            sensorsTurnoutComboBox.setToolTipText(Bundle.getMessage("SensorsTurnoutNameHint"));
11071
11072            theContentPane.add(panel1);
11073            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11074
11075            JPanel panel2 = new JPanel(new FlowLayout());
11076            JLabel shTitle = new JLabel(Bundle.getMessage("Sensors"));
11077            panel2.add(shTitle);
11078            panel2.add(new JLabel("   "));
11079            panel2.add(getSavedSensors = new JButton(Bundle.getMessage("GetSaved")));
11080            getSavedSensors.addActionListener(this::turnoutSensorsGetSaved);
11081            getSavedSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
11082            theContentPane.add(panel2);
11083
11084            sensorTurnoutPanel.setLayout(new GridLayout(0, 2)); //Content added as needed
11085            theContentPane.add(sensorTurnoutPanel);
11086
11087            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11088
11089            JPanel panel6 = new JPanel(new FlowLayout());
11090            panel6.add(changeSensorIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
11091            changeSensorIcon.addActionListener((ActionEvent e) -> turnoutSensorFrame.setVisible(true));
11092            changeSensorIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
11093            panel6.add(new JLabel("   "));
11094            panel6.add(setSensorsDone = new JButton(Bundle.getMessage("ButtonDone")));
11095            setSensorsDone.addActionListener(this::setSensorsDonePressed);
11096            setSensorsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
11097            panel6.add(setSensorsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
11098            setSensorsCancel.addActionListener(this::setSensorsCancelPressed);
11099            setSensorsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
11100            theContentPane.add(panel6);
11101            setSensorsAtTurnoutFrame.addWindowListener(new WindowAdapter() {
11102                @Override
11103                public void windowClosing(WindowEvent e) {
11104                    setSensorsCancelPressed(null);
11105                }
11106            });
11107        }
11108
11109        sensorTurnoutPanel.removeAll();
11110
11111        sensorsTurnoutComboBox.setVisible(!setSensorsAtTurnoutFromMenuFlag);
11112
11113        if (setSensorsAtTurnoutFromMenuFlag) {
11114            turnoutSensorNameLabel.setText(Bundle.getMessage("MakeLabel",
11115                    Bundle.getMessage("BeanNameTurnout")
11116                    + " " + Bundle.getMessage("Name"))
11117                    + " " + layoutTurnout.getTurnoutName());
11118            turnoutSensorsGetSaved(null);
11119        } else {
11120            turnoutSensorNameLabel.setText(Bundle.getMessage("BeanNameTurnout") + " "
11121                    + Bundle.getMessage("Name"));
11122        }
11123
11124        if (!setSensorsAtTurnoutOpenFlag) {
11125            setSensorsAtTurnoutFrame.setPreferredSize(null);
11126            setSensorsAtTurnoutFrame.pack();
11127            setSensorsAtTurnoutOpenFlag = true;
11128        }
11129        setSensorsAtTurnoutFrame.setVisible(true);
11130    }   //setSensorsAtTurnout
11131
11132    private void turnoutSensorsGetSaved(ActionEvent a) {
11133        if (!getTurnoutSensorInformation()) {
11134            return;
11135        }
11136        turnoutSenBlocks = layoutTurnout.getBlockBoundaries();
11137
11138        turnoutSensorA.setTextField(layoutTurnout.getSensorAName());
11139        turnoutSensorB.setTextField(layoutTurnout.getSensorBName());
11140        turnoutSensorC.setTextField(layoutTurnout.getSensorCName());
11141        turnoutSensorD.setTextField(layoutTurnout.getSensorDName());
11142
11143        turnoutSensorA.setBoundaryLabel(turnoutSenBlocks[0]);
11144        turnoutSensorB.setBoundaryLabel(turnoutSenBlocks[1]);
11145        turnoutSensorC.setBoundaryLabel(turnoutSenBlocks[2]);
11146        turnoutSensorD.setBoundaryLabel(turnoutSenBlocks[3]);
11147
11148        sensorTurnoutPanel.remove(turnoutSensorA.getDetailsPanel());
11149        sensorTurnoutPanel.remove(turnoutSensorB.getDetailsPanel());
11150        sensorTurnoutPanel.remove(turnoutSensorC.getDetailsPanel());
11151        sensorTurnoutPanel.remove(turnoutSensorD.getDetailsPanel());
11152
11153        boolean boundaryFlag = false;
11154        if (turnoutSenBlocks[0] != null) {
11155            sensorTurnoutPanel.add(turnoutSensorA.getDetailsPanel());
11156            boundaryFlag = true;
11157        }
11158        if (turnoutSenBlocks[1] != null) {
11159            sensorTurnoutPanel.add(turnoutSensorB.getDetailsPanel());
11160            boundaryFlag = true;
11161        }
11162        if (turnoutSenBlocks[2] != null) {
11163            sensorTurnoutPanel.add(turnoutSensorC.getDetailsPanel());
11164            boundaryFlag = true;
11165        }
11166        if (turnoutSenBlocks[3] != null) {
11167            sensorTurnoutPanel.add(turnoutSensorD.getDetailsPanel());
11168            boundaryFlag = true;
11169        }
11170        if (!boundaryFlag) {
11171            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame, "There are no block boundaries on this turnout\nIt is therefore not possible to add Sensors to it");
11172        }
11173        setSensorsAtTurnoutFrame.setPreferredSize(null);
11174        setSensorsAtTurnoutFrame.pack();
11175    }   //turnoutSensorsGetSaved
11176
11177    SensorIcon turnoutSensorBlockIcon;
11178
11179    private void setSensorsDonePressed(ActionEvent a) {
11180        log.trace("setSensorsDonePressed (turnouts)");  // NOI18N
11181        if (!getTurnoutSensorInformation()) {
11182            return;
11183        }
11184
11185        //process sensor names
11186        Sensor sensorA = getSensorFromEntry(turnoutSensorA.getText(), false, setSensorsAtTurnoutFrame);
11187        Sensor sensorB = getSensorFromEntry(turnoutSensorB.getText(), false, setSensorsAtTurnoutFrame);
11188        Sensor sensorC = getSensorFromEntry(turnoutSensorC.getText(), false, setSensorsAtTurnoutFrame);
11189        Sensor sensorD = getSensorFromEntry(turnoutSensorD.getText(), false, setSensorsAtTurnoutFrame);
11190
11191        Sensor currSensorA = layoutTurnout.getSensorA();
11192        Sensor currSensorB = layoutTurnout.getSensorB();
11193        Sensor currSensorC = layoutTurnout.getSensorC();
11194        Sensor currSensorD = layoutTurnout.getSensorD();
11195
11196        if (log.isTraceEnabled()) {
11197            log.trace("current sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11198                    (currSensorA == null) ? "- none- " : currSensorA.getDisplayName(), // NOI18N
11199                    (currSensorB == null) ? "- none- " : currSensorB.getDisplayName(), // NOI18N
11200                    (currSensorC == null) ? "- none- " : currSensorC.getDisplayName(), // NOI18N
11201                    (currSensorD == null) ? "- none- " : currSensorD.getDisplayName());  // NOI18N
11202            log.trace("new sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11203                    (sensorA == null) ? "- none- " : sensorA.getDisplayName(), // NOI18N
11204                    (sensorB == null) ? "- none- " : sensorB.getDisplayName(), // NOI18N
11205                    (sensorC == null) ? "- none- " : sensorC.getDisplayName(), // NOI18N
11206                    (sensorD == null) ? "- none- " : sensorD.getDisplayName());  // NOI18N
11207        }
11208
11209        LayoutTurnoutView layoutTurnoutView = layoutEditor.getLayoutTurnoutView(layoutTurnout);
11210
11211        //place/remove sensors as requested
11212        if (sensorA == null) {
11213            if (currSensorA != null && removeSensorFromPanel(currSensorA)) {
11214                layoutTurnout.setSensorA(null);
11215            }
11216        } else if (turnoutSensorA != null && layoutTurnout.getConnectA() != null) {
11217            setTurnoutSensor(layoutTurnout, sensorA, currSensorA, turnoutSensorA, layoutTurnout.getConnectA(), layoutTurnoutView.getCoordsA(), "A");
11218        }
11219
11220        if (sensorB == null) {
11221            if (currSensorB != null && removeSensorFromPanel(currSensorB)) {
11222                layoutTurnout.setSensorB(null);
11223            }
11224        } else if (turnoutSensorB != null && layoutTurnout.getConnectB() != null) {
11225            setTurnoutSensor(layoutTurnout, sensorB, currSensorB, turnoutSensorB, layoutTurnout.getConnectB(), layoutTurnoutView.getCoordsB(), "B");
11226        }
11227
11228        if (sensorC == null) {
11229            if (currSensorC != null && removeSensorFromPanel(currSensorC)) {
11230                layoutTurnout.setSensorC(null);
11231            }
11232        } else if (turnoutSensorC != null && layoutTurnout.getConnectC() != null) {
11233            setTurnoutSensor(layoutTurnout, sensorC, currSensorC, turnoutSensorC, layoutTurnout.getConnectC(), layoutTurnoutView.getCoordsC(), "C");
11234        }
11235
11236        if (sensorD == null) {
11237            if (currSensorD != null && removeSensorFromPanel(currSensorD)) {
11238                layoutTurnout.setSensorD(null);
11239            }
11240        } else if (turnoutSensorD != null && layoutTurnout.getConnectD() != null) {
11241            setTurnoutSensor(layoutTurnout, sensorD, currSensorD, turnoutSensorD, layoutTurnout.getConnectD(), layoutTurnoutView.getCoordsD(), "D");
11242        }
11243
11244        //make sure this layout turnout is not linked to another
11245        layoutTurnout.setLinkType(LayoutTurnout.LinkType.NO_LINK);
11246        layoutTurnout.setLinkedTurnoutName("");
11247
11248        //finish up
11249        setSensorsAtTurnoutOpenFlag = false;
11250        setSensorsAtTurnoutFrame.setVisible(false);
11251        if (needRedraw) {
11252            layoutEditor.redrawPanel();
11253            needRedraw = false;
11254            layoutEditor.setDirty();
11255        }
11256    }   //setSensorsDonePressed
11257
11258    /**
11259     * Attached a sensor to a turnout block boundary. Supports both
11260     * LayoutTurnout and LayoutSlip classes.
11261     *
11262     * @since 4.11.2
11263     * @param <T>        The specific type, a subtype of LayoutTurnout
11264     * @param trackItem  The turnout or slip that is being modified.
11265     * @param newSensor  The sensor that is being added.
11266     * @param currSensor The sensor that might already be there, otherwise null.
11267     * @param beanDetail The BeanDetails object that contains the supporting
11268     *                   data.
11269     * @param connect    The track segment that is attached to this point
11270     * @param coords     The track componennt coordinates
11271     * @param position   Which of the four points is being changed
11272     */
11273    <T extends LayoutTurnout> void setTurnoutSensor(T trackItem, Sensor newSensor, Sensor currSensor,
11274            BeanDetails<? extends NamedBean> beanDetail, LayoutTrack connect, Point2D coords, String position) {
11275        if (currSensor == null) {
11276            if (!isSensorAssignedAnywhere(newSensor)) {
11277                log.trace("Add sensor '{}'", newSensor.getDisplayName());  // NOI18N
11278                switch (position) {
11279                    case "A":  // NOI18N
11280                        trackItem.setSensorA(beanDetail.getText());
11281                        break;
11282                    case "B":  // NOI18N
11283                        trackItem.setSensorB(beanDetail.getText());
11284                        break;
11285                    case "C":  // NOI18N
11286                        trackItem.setSensorC(beanDetail.getText());
11287                        break;
11288                    case "D":  // NOI18N
11289                        trackItem.setSensorD(beanDetail.getText());
11290                        break;
11291                    default:
11292                        break;
11293                }
11294                if (beanDetail.addToPanel()) {
11295                    log.trace("Add icon for sensor '{}'", newSensor.getDisplayName());  // NOI18N
11296                    placingBlock(getSensorIcon(beanDetail.getText()),
11297                            beanDetail.isRightSelected(), 0.0,
11298                            connect, coords);
11299                    needRedraw = true;
11300                }
11301            } else {
11302                sensorAssignedElseWhere(newSensor);
11303            }
11304        } else if (currSensor == newSensor) {
11305            if (beanDetail.addToPanel()) {
11306                if (!isSensorOnPanel(newSensor)) {
11307                    log.trace("Add icon for existing sensor '{}'", newSensor.getDisplayName());  // NOI18N
11308                    placingBlock(getSensorIcon(beanDetail.getText()),
11309                            beanDetail.isRightSelected(), 0.0,
11310                            connect, coords);
11311                    needRedraw = true;
11312                }
11313            }
11314        } else {
11315            if (!isSensorAssignedAnywhere(newSensor)) {
11316                if (removeSensorFromPanel(currSensor)) {
11317                    log.trace("Replace sensor '{}' with sensor '{}'", // NOI18N
11318                            currSensor.getDisplayName(), newSensor.getDisplayName());
11319                    switch (position) {
11320                        case "A":  // NOI18N
11321                            trackItem.setSensorA(beanDetail.getText());
11322                            break;
11323                        case "B":  // NOI18N
11324                            trackItem.setSensorB(beanDetail.getText());
11325                            break;
11326                        case "C":  // NOI18N
11327                            trackItem.setSensorC(beanDetail.getText());
11328                            break;
11329                        case "D":  // NOI18N
11330                            trackItem.setSensorD(beanDetail.getText());
11331                            break;
11332                        default:
11333                            break;
11334                    }
11335                    if (beanDetail.addToPanel()) {
11336                        log.trace("Add icon for replacement sensor '{}'", // NOI18N
11337                                newSensor.getDisplayName());
11338                        placingBlock(getSensorIcon(beanDetail.getText()),
11339                                beanDetail.isRightSelected(), 0.0,
11340                                connect, coords);
11341                        needRedraw = true;
11342                    }
11343                }
11344            } else {
11345                sensorAssignedElseWhere(newSensor);
11346            }
11347        }
11348    }
11349
11350    private boolean getTurnoutSensorInformation() {
11351        turnout = null;
11352        layoutTurnout = null;
11353        String str = sensorsTurnoutComboBox.getSelectedItemDisplayName();
11354        if ((str == null) || str.isEmpty()) {
11355            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame, Bundle.getMessage("SensorsError1"),
11356                    Bundle.getMessage("ErrorTitle"),
11357                    JmriJOptionPane.ERROR_MESSAGE);
11358            return false;
11359        }
11360        turnout = InstanceManager.turnoutManagerInstance().getTurnout(str);
11361        if (turnout == null) {
11362            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame,
11363                    Bundle.getMessage("SensorsError2",
11364                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
11365                    JmriJOptionPane.ERROR_MESSAGE);
11366            return false;
11367        } else {
11368            String uname = turnout.getUserName();
11369            if ((uname == null) || uname.isEmpty()
11370                    || !uname.equals(str)) {
11371                sensorsTurnoutComboBox.setSelectedItem(turnout);
11372            }
11373        }
11374        layoutTurnout = layoutEditor.getFinder().findLayoutTurnoutByBean(turnout);
11375        if (layoutTurnout == null) {
11376            JmriJOptionPane.showMessageDialog(setSensorsAtTurnoutFrame,
11377                    Bundle.getMessage("SensorsError3",
11378                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
11379                    JmriJOptionPane.ERROR_MESSAGE);
11380            return false;
11381        }
11382        return true;
11383    }   //getTurnoutSensorInformation
11384
11385    private void setSensorsCancelPressed(ActionEvent a) {
11386        setSensorsAtTurnoutOpenFlag = false;
11387        setSensorsAtTurnoutFrame.setVisible(false);
11388    }
11389
11390    /*=======================*\
11391    |* setSensorsAtLevelXing *|
11392    \*=======================*/
11393    //operational variables for Set Sensors at Level Crossing tool
11394    private JmriJFrame setSensorsAtLevelXingFrame = null;
11395    private boolean setSensorsAtLevelXingOpenFlag = false;
11396    private boolean setSensorsAtLevelXingFromMenuFlag = false;
11397
11398    private JLabel xingSensorsBlockACNameLabel = null;
11399    private JLabel xingSensorsBlockBDNameLabel = null;
11400
11401    private final NamedBeanComboBox<Block> xingSensorsBlockACComboBox
11402            = new NamedBeanComboBox<>(
11403                    InstanceManager.getDefault(BlockManager.class),
11404                    null, DisplayOptions.DISPLAYNAME);
11405    private final NamedBeanComboBox<Block> xingSensorsBlockBDComboBox
11406            = new NamedBeanComboBox<>(
11407                    InstanceManager.getDefault(BlockManager.class),
11408                    null, DisplayOptions.DISPLAYNAME);
11409
11410    private JButton getSavedXingSensors = null;
11411    private JButton setXingSensorsDone = null;
11412    private JButton setXingSensorsCancel = null;
11413    private JButton changeSensorXingIcon = null;
11414    JFrame sensorXingFrame = null;
11415
11416    private String[] xingSensorBlocks = new String[4];
11417
11418    BeanDetails<Sensor> xingSensorA;
11419    BeanDetails<Sensor> xingSensorB;
11420    BeanDetails<Sensor> xingSensorC;
11421    BeanDetails<Sensor> xingSensorD;
11422
11423    JPanel sensorXingPanel = new JPanel(new FlowLayout());
11424
11425    //display dialog for Set Signals at Level Crossing tool
11426    public void setSensorsAtLevelXingFromMenu(@Nonnull LevelXing xing,
11427            @Nonnull String[] blocks,
11428            @Nonnull MultiIconEditor theEditor,
11429            @Nonnull JFrame theFrame) {
11430        levelXing = xing;
11431        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
11432        xingSensorsBlockACComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameAC()));
11433        xingSensorsBlockBDComboBox.setSelectedItem(bm.getBlock(levelXing.getBlockNameBD()));
11434        for (int i = 0; i < blocks.length; i++) {
11435            xingSensorBlocks[i] = blocks[i];
11436        }
11437        setSensorsAtLevelXingFromMenuFlag = true;
11438        setSensorsAtLevelXing(theEditor, theFrame);
11439        setSensorsAtLevelXingFromMenuFlag = false;
11440    }
11441
11442    //TODO: Add to Tools menu?
11443    public void setSensorsAtLevelXing(@Nonnull MultiIconEditor theEditor,
11444            @Nonnull JFrame theFrame) {
11445        sensorIconEditor = theEditor;
11446        sensorXingFrame = theFrame;
11447
11448        //Initialize if needed
11449        if (setSensorsAtLevelXingFrame == null) {
11450            setSensorsAtLevelXingOpenFlag = false;
11451
11452            xingSensorA = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11453            xingSensorB = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11454            xingSensorC = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11455            xingSensorD = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11456
11457            xingSensorA.getDetailsPanel().setBackground(new Color(255, 255, 200));
11458            xingSensorB.getDetailsPanel().setBackground(new Color(200, 255, 255));
11459            xingSensorC.getDetailsPanel().setBackground(new Color(200, 200, 255));
11460            xingSensorD.getDetailsPanel().setBackground(new Color(255, 200, 200));
11461
11462            setSensorsAtLevelXingFrame = new JmriJFrame(Bundle.getMessage("SensorsAtLevelXing"), false, true);
11463            oneFrameToRuleThemAll(setSensorsAtLevelXingFrame);
11464            setSensorsAtLevelXingFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
11465//         setSensorsAtLevelXingFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtLevelXing", true);
11466            setSensorsAtLevelXingFrame.setLocation(70, 30);
11467            Container theContentPane = setSensorsAtLevelXingFrame.getContentPane();
11468            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
11469
11470            JPanel panel11 = new JPanel(new FlowLayout());
11471
11472            xingSensorsBlockACNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
11473                    Bundle.getMessage("BeanNameBlock") + " AC "
11474                    + Bundle.getMessage("Name")));
11475            panel11.add(xingSensorsBlockACNameLabel);
11476            panel11.add(xingSensorsBlockACComboBox);
11477            xingSensorsBlockACComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
11478            theContentPane.add(panel11);
11479
11480            JPanel panel12 = new JPanel(new FlowLayout());
11481            xingSensorsBlockBDNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
11482                    Bundle.getMessage("BeanNameBlock") + " BD "
11483                    + Bundle.getMessage("Name")));
11484            panel12.add(xingSensorsBlockBDNameLabel);
11485            panel12.add(xingSensorsBlockBDComboBox);
11486            xingSensorsBlockBDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
11487            theContentPane.add(panel12);
11488
11489            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11490
11491            JPanel panel2 = new JPanel(new FlowLayout());
11492            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSensor"));
11493            panel2.add(shTitle);
11494            panel2.add(new JLabel("   "));
11495            panel2.add(getSavedXingSensors = new JButton(Bundle.getMessage("GetSaved")));
11496            getSavedXingSensors.addActionListener(this::xingSensorsGetSaved);
11497            getSavedXingSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
11498            theContentPane.add(panel2);
11499
11500            sensorXingPanel.setLayout(new GridLayout(0, 2));
11501            theContentPane.add(sensorXingPanel);
11502            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
11503
11504            JPanel panel6 = new JPanel(new FlowLayout());
11505            panel6.add(changeSensorXingIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
11506            changeSensorXingIcon.addActionListener((ActionEvent e) -> sensorXingFrame.setVisible(true));
11507            changeSensorXingIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
11508
11509            panel6.add(new JLabel("   "));
11510            panel6.add(setXingSensorsDone = new JButton(Bundle.getMessage("ButtonDone")));
11511            setXingSensorsDone.addActionListener(this::setXingSensorsDonePressed);
11512            setXingSensorsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
11513            panel6.add(setXingSensorsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
11514            setXingSensorsCancel.addActionListener(this::setXingSensorsCancelPressed);
11515            setXingSensorsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
11516            theContentPane.add(panel6);
11517            setSensorsAtLevelXingFrame.addWindowListener(new WindowAdapter() {
11518                @Override
11519                public void windowClosing(WindowEvent e) {
11520                    setXingSensorsCancelPressed(null);
11521                }
11522            });
11523        }
11524
11525        sensorXingPanel.removeAll();
11526
11527        xingSensorsBlockACComboBox.setVisible(!setSensorsAtLevelXingFromMenuFlag);
11528        xingSensorsBlockBDComboBox.setVisible(!setSensorsAtLevelXingFromMenuFlag);
11529
11530        if (setSensorsAtLevelXingFromMenuFlag) {
11531            xingSensorsBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
11532                    Bundle.getMessage("BeanNameBlock") + " AC "
11533                    + Bundle.getMessage("Name")) + " " + levelXing.getBlockNameAC());
11534            xingSensorsBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
11535                    Bundle.getMessage("BeanNameBlock") + " BD "
11536                    + Bundle.getMessage("Name")) + " " + levelXing.getBlockNameBD());
11537
11538            xingSensorA.setTextField(levelXing.getSensorAName());
11539            xingSensorB.setTextField(levelXing.getSensorBName());
11540            xingSensorC.setTextField(levelXing.getSensorCName());
11541            xingSensorD.setTextField(levelXing.getSensorDName());
11542            xingSensorsGetSaved(null);
11543        } else {
11544            xingSensorsBlockACNameLabel.setText(Bundle.getMessage("MakeLabel",
11545                    Bundle.getMessage("BeanNameBlock") + " AC "
11546                    + Bundle.getMessage("Name")));
11547            xingSensorsBlockBDNameLabel.setText(Bundle.getMessage("MakeLabel",
11548                    Bundle.getMessage("BeanNameBlock") + " BD "
11549                    + Bundle.getMessage("Name")));
11550        }
11551
11552        if (!setSensorsAtLevelXingOpenFlag) {
11553            setSensorsAtLevelXingFrame.setPreferredSize(null);
11554            setSensorsAtLevelXingFrame.pack();
11555            setSensorsAtLevelXingOpenFlag = true;
11556        }
11557        setSensorsAtLevelXingFrame.setVisible(true);
11558    }
11559
11560    private void xingSensorsGetSaved(ActionEvent a) {
11561        if (!getLevelCrossingSensorInformation()) {
11562            return;
11563        }
11564
11565        xingSensorBlocks = levelXing.getBlockBoundaries();
11566
11567        xingSensorA.setTextField(levelXing.getSensorAName());
11568        xingSensorB.setTextField(levelXing.getSensorBName());
11569        xingSensorC.setTextField(levelXing.getSensorCName());
11570        xingSensorD.setTextField(levelXing.getSensorDName());
11571
11572        sensorXingPanel.remove(xingSensorA.getDetailsPanel());
11573        sensorXingPanel.remove(xingSensorB.getDetailsPanel());
11574        sensorXingPanel.remove(xingSensorC.getDetailsPanel());
11575        sensorXingPanel.remove(xingSensorD.getDetailsPanel());
11576
11577        xingSensorA.setBoundaryLabel(xingSensorBlocks[0]);
11578        xingSensorB.setBoundaryLabel(xingSensorBlocks[1]);
11579        xingSensorC.setBoundaryLabel(xingSensorBlocks[2]);
11580        xingSensorD.setBoundaryLabel(xingSensorBlocks[3]);
11581
11582        boolean boundaryFlag = false;
11583        if (xingSensorBlocks[0] != null) {
11584            sensorXingPanel.add(xingSensorA.getDetailsPanel());
11585            boundaryFlag = true;
11586        }
11587        if (xingSensorBlocks[1] != null) {
11588            sensorXingPanel.add(xingSensorB.getDetailsPanel());
11589            boundaryFlag = true;
11590        }
11591        if (xingSensorBlocks[2] != null) {
11592            sensorXingPanel.add(xingSensorC.getDetailsPanel());
11593            boundaryFlag = true;
11594        }
11595        if (xingSensorBlocks[3] != null) {
11596            sensorXingPanel.add(xingSensorD.getDetailsPanel());
11597            boundaryFlag = true;
11598        }
11599        if (!boundaryFlag) {
11600            JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame, Bundle.getMessage("NoBoundaryXingSensor"));
11601        }
11602        setSensorsAtLevelXingFrame.setPreferredSize(null);
11603        setSensorsAtLevelXingFrame.pack();
11604    }
11605
11606    private boolean getLevelCrossingSensorInformation() {
11607        if (!setSensorsAtLevelXingFromMenuFlag) {
11608            levelXing = null;
11609            List<LevelXing> levelXings = layoutEditor.getLevelXings();
11610            if (levelXings.size() <= 0) {
11611                JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame,
11612                        Bundle.getMessage("SignalsError15"),
11613                        Bundle.getMessage("ErrorTitle"),
11614                        JmriJOptionPane.ERROR_MESSAGE);
11615                return false;
11616            } else if (levelXings.size() == 1) {
11617                levelXing = levelXings.get(0);
11618            } else {
11619                LayoutBlock xingSensorBlockA = null;
11620                LayoutBlock xingSensorBlockC = null;
11621                xingSensorBlockA = getBlockFromEntry(xingSensorsBlockACComboBox);
11622                if (xingSensorBlockA == null) {
11623                    return false;
11624                }
11625                String theBlockName = xingSensorsBlockBDComboBox.getSelectedItemDisplayName();
11626                if ((theBlockName != null) && !theBlockName.isEmpty()) {
11627                    xingSensorBlockC = getBlockFromEntry(xingSensorsBlockBDComboBox);
11628                    if (xingSensorBlockC == null) {
11629                        return false;
11630                    }
11631                }
11632
11633                int foundCount = 0;
11634                //make two block tests first
11635                if (xingSensorBlockC != null) {
11636                    for (LevelXing x : layoutEditor.getLevelXings()) {
11637                        LayoutBlock xA = null;
11638                        LayoutBlock xB = null;
11639                        LayoutBlock xC = null;
11640                        LayoutBlock xD = null;
11641                        LayoutBlock xAC = x.getLayoutBlockAC();
11642                        LayoutBlock xBD = x.getLayoutBlockBD();
11643                        if (x.getConnectA() != null) {
11644                            xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
11645                        }
11646                        if (x.getConnectB() != null) {
11647                            xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
11648                        }
11649                        if (x.getConnectC() != null) {
11650                            xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
11651                        }
11652                        if (x.getConnectD() != null) {
11653                            xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
11654                        }
11655                        if (((xA != null) && (xC != null) && (((xA == xingSensorBlockA) && (xC == xingSensorBlockC))
11656                                || ((xA == xingSensorBlockC) && (xC == xingSensorBlockA))))
11657                                || ((xB != null) && (xD != null) && (((xB == xingSensorBlockA) && (xD == xingSensorBlockC))
11658                                || ((xB == xingSensorBlockC) && (xD == xingSensorBlockA))))) {
11659                            levelXing = x;
11660                            foundCount++;
11661                        } else if ((xAC != null) && (xBD != null) && (((xAC == xingSensorBlockA) && (xBD == xingSensorBlockC))
11662                                || ((xAC == xingSensorBlockC) && (xBD == xingSensorBlockA)))) {
11663                            levelXing = x;
11664                            foundCount++;
11665                        }
11666                    }
11667                }
11668                if (foundCount == 0) {
11669                    //try one block test
11670                    for (LevelXing x : layoutEditor.getLevelXings()) {
11671                        if ((xingSensorBlockA == x.getLayoutBlockAC()) || (xingSensorBlockA == x.getLayoutBlockBD())) {
11672                            levelXing = x;
11673                            foundCount++;
11674                        }
11675                    }
11676                }
11677                if (foundCount > 1) {
11678                    JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame,
11679                            Bundle.getMessage("SignalsError16",
11680                                    new Object[]{" " + foundCount + " "}),
11681                            Bundle.getMessage("ErrorTitle"),
11682                            JmriJOptionPane.ERROR_MESSAGE);
11683                    return false;
11684                }
11685                if (levelXing == null) {
11686                    JmriJOptionPane.showMessageDialog(setSensorsAtLevelXingFrame,
11687                            Bundle.getMessage("SignalsError17"),
11688                            Bundle.getMessage("ErrorTitle"),
11689                            JmriJOptionPane.ERROR_MESSAGE);
11690                    return false;
11691                }
11692            }
11693        }
11694        return true;
11695    }
11696
11697    private void setXingSensorsCancelPressed(ActionEvent a) {
11698        setSensorsAtLevelXingOpenFlag = false;
11699        setSensorsAtLevelXingFrame.setVisible(false);
11700    }
11701
11702    private void setXingSensorsDonePressed(ActionEvent a) {
11703        log.trace("setXingSensorsDonePressed");  // NOI18N
11704
11705        if (!getLevelCrossingSensorInformation()) {
11706            return;
11707        }
11708
11709        Sensor aSensor = getSensorFromEntry(xingSensorA.getText(), false, setSensorsAtLevelXingFrame);
11710        Sensor bSensor = getSensorFromEntry(xingSensorB.getText(), false, setSensorsAtLevelXingFrame);
11711        Sensor cSensor = getSensorFromEntry(xingSensorC.getText(), false, setSensorsAtLevelXingFrame);
11712        Sensor dSensor = getSensorFromEntry(xingSensorD.getText(), false, setSensorsAtLevelXingFrame);
11713
11714        Sensor currSensorA = levelXing.getSensorA();
11715        Sensor currSensorB = levelXing.getSensorB();
11716        Sensor currSensorC = levelXing.getSensorC();
11717        Sensor currSensorD = levelXing.getSensorD();
11718
11719        if (log.isTraceEnabled()) {
11720            log.trace("current sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11721                    (currSensorA == null) ? "- none- " : currSensorA.getDisplayName(), // NOI18N
11722                    (currSensorB == null) ? "- none- " : currSensorB.getDisplayName(), // NOI18N
11723                    (currSensorC == null) ? "- none- " : currSensorC.getDisplayName(), // NOI18N
11724                    (currSensorD == null) ? "- none- " : currSensorD.getDisplayName());  // NOI18N
11725            log.trace("new sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
11726                    (aSensor == null) ? "- none- " : aSensor.getDisplayName(), // NOI18N
11727                    (bSensor == null) ? "- none- " : bSensor.getDisplayName(), // NOI18N
11728                    (cSensor == null) ? "- none- " : cSensor.getDisplayName(), // NOI18N
11729                    (dSensor == null) ? "- none- " : dSensor.getDisplayName());  // NOI18N
11730        }
11731
11732        LevelXingView levelXingView = layoutEditor.getLevelXingView(levelXing);
11733        // place/remove sensors as requested
11734        if (aSensor == null) {
11735            if (currSensorA != null && removeSensorFromPanel(currSensorA)) {
11736                levelXing.setSensorAName(null);
11737            }
11738        } else if (xingSensorA != null && levelXing.getConnectA() != null) {
11739            setLevelXingSensor(aSensor, currSensorA, xingSensorA, levelXing.getConnectA(), levelXingView.getCoordsA(), "A");
11740        }
11741
11742        if (bSensor == null) {
11743            if (currSensorB != null && removeSensorFromPanel(currSensorB)) {
11744                levelXing.setSensorBName(null);
11745            }
11746        } else if (xingSensorB != null && levelXing.getConnectB() != null) {
11747            setLevelXingSensor(bSensor, currSensorB, xingSensorB, levelXing.getConnectB(), levelXingView.getCoordsB(), "B");
11748        }
11749
11750        if (cSensor == null) {
11751            if (currSensorC != null && removeSensorFromPanel(currSensorC)) {
11752                levelXing.setSensorCName(null);
11753            }
11754        } else if (xingSensorC != null && levelXing.getConnectC() != null) {
11755            setLevelXingSensor(cSensor, currSensorC, xingSensorC, levelXing.getConnectC(), levelXingView.getCoordsC(), "C");
11756        }
11757
11758        if (dSensor == null) {
11759            if (currSensorD != null && removeSensorFromPanel(currSensorD)) {
11760                levelXing.setSensorDName(null);
11761            }
11762        } else if (xingSensorD != null && levelXing.getConnectD() != null) {
11763            setLevelXingSensor(dSensor, currSensorD, xingSensorD, levelXing.getConnectD(), levelXingView.getCoordsD(), "D");
11764        }
11765
11766        //setup logic if requested
11767        //finish up
11768        setSensorsAtLevelXingOpenFlag = false;
11769        setSensorsAtLevelXingFrame.setVisible(false);
11770        if (needRedraw) {
11771            layoutEditor.redrawPanel();
11772            needRedraw = false;
11773            layoutEditor.setDirty();
11774        }
11775    }
11776
11777    /**
11778     * Attached a sensor to a level crossing block boundary.
11779     *
11780     * @since 4.11.2
11781     * @param newSensor  The sensor that is being added.
11782     * @param currSensor The sensor that might already be there, otherwise null.
11783     * @param beanDetail The BeanDetails object that contains the supporting
11784     *                   data.
11785     * @param connect    The track segment that is attached to this point
11786     * @param coords     The track componennt coordinates
11787     * @param position   Which of the four points is being changed
11788     */
11789    void setLevelXingSensor(Sensor newSensor, Sensor currSensor, BeanDetails<? extends NamedBean> beanDetail,
11790            LayoutTrack connect, Point2D coords, String position) {
11791        if (currSensor == null) {
11792            if (!isSensorAssignedAnywhere(newSensor)) {
11793                log.trace("Add sensor '{}'", newSensor.getDisplayName());  // NOI18N
11794                switch (position) {
11795                    case "A":  // NOI18N
11796                        levelXing.setSensorAName(beanDetail.getText());
11797                        break;
11798                    case "B":  // NOI18N
11799                        levelXing.setSensorBName(beanDetail.getText());
11800                        break;
11801                    case "C":  // NOI18N
11802                        levelXing.setSensorCName(beanDetail.getText());
11803                        break;
11804                    case "D":  // NOI18N
11805                        levelXing.setSensorDName(beanDetail.getText());
11806                        break;
11807                    default:
11808                        break;
11809                }
11810                if (beanDetail.addToPanel()) {
11811                    log.trace("Add icon for sensor '{}'", newSensor.getDisplayName());  // NOI18N
11812                    placingBlock(getSensorIcon(beanDetail.getText()),
11813                            beanDetail.isRightSelected(), 0.0, connect, coords);
11814                    needRedraw = true;
11815                }
11816            } else {
11817                sensorAssignedElseWhere(newSensor);
11818            }
11819        } else if (currSensor == newSensor) {
11820            if (beanDetail.addToPanel()) {
11821                if (!isSensorOnPanel(newSensor)) {
11822                    log.trace("Add icon for existing sensor '{}'", newSensor.getDisplayName());  // NOI18N
11823                    placingBlock(getSensorIcon(beanDetail.getText()),
11824                            beanDetail.isRightSelected(), 0.0, connect, coords);
11825                    needRedraw = true;
11826                }
11827            }
11828        } else {
11829            if (!isSensorAssignedAnywhere(newSensor)) {
11830                if (removeSensorFromPanel(currSensor)) {
11831                    log.trace("Replace sensor '{}' with sensor '{}'", // NOI18N
11832                            currSensor.getDisplayName(), newSensor.getDisplayName());
11833                    switch (position) {
11834                        case "A":  // NOI18N
11835                            levelXing.setSensorAName(beanDetail.getText());
11836                            break;
11837                        case "B":  // NOI18N
11838                            levelXing.setSensorBName(beanDetail.getText());
11839                            break;
11840                        case "C":  // NOI18N
11841                            levelXing.setSensorCName(beanDetail.getText());
11842                            break;
11843                        case "D":  // NOI18N
11844                            levelXing.setSensorDName(beanDetail.getText());
11845                            break;
11846                        default:
11847                            break;
11848                    }
11849                    if (beanDetail.addToPanel()) {
11850                        log.trace("Add icon for replacement sensor '{}'", // NOI18N
11851                                newSensor.getDisplayName());
11852                        placingBlock(getSensorIcon(beanDetail.getText()),
11853                                beanDetail.isRightSelected(), 0.0, connect, coords);
11854                        needRedraw = true;
11855                    }
11856                }
11857            } else {
11858                sensorAssignedElseWhere(newSensor);
11859            }
11860        }
11861    }
11862
11863    private boolean getSimpleBlockInformation() {
11864        //might have to do something to trick it with an end bumper
11865        if (!setSignalMastsAtBlockBoundaryFromMenuFlag) {
11866            block1 = getBlockFromEntry(block1IDComboBox);
11867            if (block1 == null) {
11868                return false;
11869            }
11870            block2 = getBlockFromEntry(block2IDComboBox);
11871            boundary = null;
11872            //if block2 is undefined or same as block 1
11873            if (block2 == null || (block1 == block2)) {
11874                //find the 1st positionablePoint that's connect1'ed to block1
11875                for (PositionablePoint p : layoutEditor.getPositionablePoints()) {
11876                    if (p.getType() == PositionablePoint.PointType.END_BUMPER) {
11877                        if (p.getConnect1() != null && p.getConnect1().getLayoutBlock() == block1) {
11878                            boundary = p;
11879                            break;
11880                        }
11881                    }
11882                }
11883            }
11884
11885            //now we try to find an anchor that connected to blocks 1 and 2
11886            //(if this fails boundary will still be set to the pp set if
11887            //block2 was null or equal to block1 above.)
11888            for (PositionablePoint p : layoutEditor.getPositionablePoints()) {
11889                if (p.getType() != PositionablePoint.PointType.END_BUMPER) {
11890                    LayoutBlock bA = null;
11891                    LayoutBlock bB = null;
11892                    if (p.getConnect1() != null) {
11893                        bA = p.getConnect1().getLayoutBlock();
11894                    }
11895                    if (p.getConnect2() != null) {
11896                        bB = p.getConnect2().getLayoutBlock();
11897                    }
11898                    if ((bA != null) && (bB != null) && (bA != bB)) {
11899                        if (((bA == block1) && (bB == block2))
11900                                || ((bA == block2) && (bB == block1))) {
11901                            boundary = p;
11902                            break;
11903                        }
11904                    }
11905                }
11906            }
11907            //if all that failed...
11908            if (boundary == null) {
11909                JmriJOptionPane.showMessageDialog(setSignalsAtBlockBoundaryFrame,
11910                        Bundle.getMessage("SignalsError7"),
11911                        Bundle.getMessage("ErrorTitle"),
11912                        JmriJOptionPane.ERROR_MESSAGE);
11913                return false;
11914            }
11915        }
11916        return true;
11917    }
11918
11919    /*==================*\
11920    |* setSensorsAtSlip *|
11921    \*==================*/
11922    //operational variables for Set Sensors at Slip tool
11923    private JmriJFrame setSensorsAtSlipFrame = null;
11924    private boolean setSensorsAtSlipOpenFlag = false;
11925    private boolean setSensorsAtSlipFromMenuFlag = false;
11926
11927    private JButton getSavedSlipSensors = null;
11928    private JButton setSlipSensorsDone = null;
11929    private JButton setSlipSensorsCancel = null;
11930    private JButton changeSensorSlipIcon = null;
11931    JFrame sensorSlipFrame = null;
11932
11933    private final NamedBeanComboBox<Block> slipSensorsBlockAComboBox
11934            = new NamedBeanComboBox<>(
11935                    InstanceManager.getDefault(BlockManager.class),
11936                    null, DisplayOptions.DISPLAYNAME);
11937    private final NamedBeanComboBox<Block> slipSensorsBlockBComboBox
11938            = new NamedBeanComboBox<>(
11939                    InstanceManager.getDefault(BlockManager.class),
11940                    null, DisplayOptions.DISPLAYNAME);
11941    private final NamedBeanComboBox<Block> slipSensorsBlockCComboBox
11942            = new NamedBeanComboBox<>(
11943                    InstanceManager.getDefault(BlockManager.class),
11944                    null, DisplayOptions.DISPLAYNAME);
11945    private final NamedBeanComboBox<Block> slipSensorsBlockDComboBox
11946            = new NamedBeanComboBox<>(
11947                    InstanceManager.getDefault(BlockManager.class),
11948                    null, DisplayOptions.DISPLAYNAME);
11949
11950    private String[] slipSensorBlocks = new String[4];
11951
11952    BeanDetails<Sensor> slipSensorA;
11953    BeanDetails<Sensor> slipSensorB;
11954    BeanDetails<Sensor> slipSensorC;
11955    BeanDetails<Sensor> slipSensorD;
11956
11957    JPanel sensorSlipPanel = new JPanel(new FlowLayout());
11958
11959    //display dialog for Set Signals at Level Crossing tool
11960    public void setSensorsAtSlipFromMenu(@Nonnull LayoutSlip slip,
11961            @Nonnull String[] blocks,
11962            @Nonnull MultiIconEditor theEditor,
11963            @Nonnull JFrame theFrame) {
11964        layoutSlip = slip;
11965        BlockManager bm = InstanceManager.getDefault(BlockManager.class);
11966        slipSensorsBlockAComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockName()));
11967        slipSensorsBlockBComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockBName()));
11968        slipSensorsBlockCComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockCName()));
11969        slipSensorsBlockDComboBox.setSelectedItem(bm.getBlock(layoutSlip.getBlockDName()));
11970        for (int i = 0; i < blocks.length; i++) {
11971            slipSensorBlocks[i] = blocks[i];
11972        }
11973        setSensorsAtSlipFromMenuFlag = true;
11974        setSensorsAtSlip(theEditor, theFrame);
11975        setSensorsAtSlipFromMenuFlag = false;
11976    }
11977
11978    //TODO: Add to Tools menu?
11979    public void setSensorsAtSlip(@Nonnull MultiIconEditor theEditor,
11980            @Nonnull JFrame theFrame) {
11981        sensorIconEditor = theEditor;
11982        sensorSlipFrame = theFrame;
11983
11984        //Initialize if needed
11985        if (setSensorsAtSlipFrame == null) {
11986            setSensorsAtSlipOpenFlag = false;
11987
11988            slipSensorA = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11989            slipSensorB = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11990            slipSensorC = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11991            slipSensorD = new BeanDetails<>("Sensor", InstanceManager.sensorManagerInstance());
11992
11993            slipSensorA.getDetailsPanel().setBackground(new Color(255, 255, 200));
11994            slipSensorB.getDetailsPanel().setBackground(new Color(200, 255, 255));
11995            slipSensorC.getDetailsPanel().setBackground(new Color(200, 200, 255));
11996            slipSensorD.getDetailsPanel().setBackground(new Color(255, 200, 200));
11997
11998            setSensorsAtSlipFrame = new JmriJFrame(Bundle.getMessage("SensorsAtSlip"), false, true);
11999            oneFrameToRuleThemAll(setSensorsAtSlipFrame);
12000            setSensorsAtSlipFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
12001//         setSensorsAtSlipFrame.addHelpMenu("package.jmri.jmrit.display.SetSensorsAtLevelSlip", true);
12002            setSensorsAtSlipFrame.setLocation(70, 30);
12003            Container theContentPane = setSensorsAtSlipFrame.getContentPane();
12004            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
12005
12006            JPanel panel11A = new JPanel(new FlowLayout());
12007            slipSignalBlockANameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12008                    Bundle.getMessage("BeanNameBlock") + " A "
12009                    + Bundle.getMessage("Name")));
12010            panel11A.add(slipSignalBlockANameLabel);
12011            panel11A.add(slipSensorsBlockAComboBox);
12012            slipSensorsBlockAComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12013            theContentPane.add(panel11A);
12014
12015            JPanel panel11B = new JPanel(new FlowLayout());
12016            slipSignalBlockBNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12017                    Bundle.getMessage("BeanNameBlock") + " B "
12018                    + Bundle.getMessage("Name")));
12019            panel11B.add(slipSignalBlockBNameLabel);
12020            panel11B.add(slipSensorsBlockBComboBox);
12021            slipSensorsBlockBComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12022            theContentPane.add(panel11B);
12023
12024            JPanel panel11C = new JPanel(new FlowLayout());
12025            slipSignalBlockCNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12026                    Bundle.getMessage("BeanNameBlock") + " C "
12027                    + Bundle.getMessage("Name")));
12028            panel11C.add(slipSignalBlockCNameLabel);
12029            panel11C.add(slipSensorsBlockCComboBox);
12030            slipSensorsBlockCComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12031            theContentPane.add(panel11C);
12032
12033            JPanel panel11D = new JPanel(new FlowLayout());
12034            slipSignalBlockDNameLabel = new JLabel(Bundle.getMessage("MakeLabel",
12035                    Bundle.getMessage("BeanNameBlock") + " D "
12036                    + Bundle.getMessage("Name")));
12037            panel11D.add(slipSignalBlockDNameLabel);
12038            panel11D.add(slipSensorsBlockDComboBox);
12039            slipSensorsBlockDComboBox.setToolTipText(Bundle.getMessage("SensorsBlockNameHint"));
12040            theContentPane.add(panel11D);
12041
12042            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12043
12044            JPanel panel2 = new JPanel(new FlowLayout());
12045            JLabel shTitle = new JLabel(Bundle.getMessage("BeanNameSensor"));
12046            panel2.add(shTitle);
12047            panel2.add(new JLabel("   "));
12048            panel2.add(getSavedSlipSensors = new JButton(Bundle.getMessage("GetSaved")));
12049            getSavedSlipSensors.addActionListener(this::slipSensorsGetSaved);
12050            getSavedSlipSensors.setToolTipText(Bundle.getMessage("GetSavedHint"));
12051            theContentPane.add(panel2);
12052
12053            sensorSlipPanel.setLayout(new GridLayout(0, 2));
12054            theContentPane.add(sensorSlipPanel);
12055            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12056
12057            JPanel panel6 = new JPanel(new FlowLayout());
12058            panel6.add(changeSensorSlipIcon = new JButton(Bundle.getMessage("ChangeSensorIcon")));
12059            changeSensorSlipIcon.addActionListener((ActionEvent e) -> sensorSlipFrame.setVisible(true));
12060            changeSensorSlipIcon.setToolTipText(Bundle.getMessage("ChangeSensorIconHint"));
12061
12062            panel6.add(new JLabel("   "));
12063            panel6.add(setSlipSensorsDone = new JButton(Bundle.getMessage("ButtonDone")));
12064            setSlipSensorsDone.addActionListener(this::setSlipSensorsDonePressed);
12065            setSlipSensorsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
12066            panel6.add(setSlipSensorsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
12067            setSlipSensorsCancel.addActionListener(this::setSlipSensorsCancelPressed);
12068            setSlipSensorsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
12069            theContentPane.add(panel6);
12070            setSensorsAtSlipFrame.addWindowListener(new WindowAdapter() {
12071                @Override
12072                public void windowClosing(WindowEvent e) {
12073                    setSlipSensorsCancelPressed(null);
12074                }
12075            });
12076        }   //if (setSensorsAtSlipFrame == null)
12077
12078        sensorSlipPanel.removeAll();
12079
12080        slipSensorsBlockAComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12081        slipSensorsBlockBComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12082        slipSensorsBlockCComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12083        slipSensorsBlockDComboBox.setVisible(!setSensorsAtSlipFromMenuFlag);
12084
12085        if (setSensorsAtSlipFromMenuFlag) {
12086            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
12087                    Bundle.getMessage("BeanNameBlock") + " A "
12088                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockName());
12089            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
12090                    Bundle.getMessage("BeanNameBlock") + " B "
12091                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockBName());
12092            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
12093                    Bundle.getMessage("BeanNameBlock") + " C "
12094                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockCName());
12095            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
12096                    Bundle.getMessage("BeanNameBlock") + " D "
12097                    + Bundle.getMessage("Name")) + " " + layoutSlip.getBlockDName());
12098            slipSensorsGetSaved(null);
12099        } else {
12100            slipSignalBlockANameLabel.setText(Bundle.getMessage("MakeLabel",
12101                    Bundle.getMessage("BeanNameBlock") + " A "
12102                    + Bundle.getMessage("Name")));
12103            slipSignalBlockBNameLabel.setText(Bundle.getMessage("MakeLabel",
12104                    Bundle.getMessage("BeanNameBlock") + " B "
12105                    + Bundle.getMessage("Name")));
12106            slipSignalBlockCNameLabel.setText(Bundle.getMessage("MakeLabel",
12107                    Bundle.getMessage("BeanNameBlock") + " C "
12108                    + Bundle.getMessage("Name")));
12109            slipSignalBlockDNameLabel.setText(Bundle.getMessage("MakeLabel",
12110                    Bundle.getMessage("BeanNameBlock") + " D "
12111                    + Bundle.getMessage("Name")));
12112        }
12113
12114        if (setSensorsAtSlipOpenFlag) {
12115            slipSensorsGetSaved(null);
12116        } else {
12117            setSensorsAtSlipFrame.setPreferredSize(null);
12118            setSensorsAtSlipFrame.setVisible(true);
12119            setSensorsAtSlipOpenFlag = true;
12120        }
12121        setSensorsAtSlipFrame.setPreferredSize(null);
12122        setSensorsAtSlipFrame.pack();
12123    }
12124
12125    private void slipSensorsGetSaved(ActionEvent a) {
12126        if (!getSlipSensorInformation()) {
12127            return;
12128        }
12129
12130        slipSensorBlocks = layoutSlip.getBlockBoundaries();
12131
12132        slipSensorA.setTextField(layoutSlip.getSensorAName());
12133        slipSensorB.setTextField(layoutSlip.getSensorBName());
12134        slipSensorC.setTextField(layoutSlip.getSensorCName());
12135        slipSensorD.setTextField(layoutSlip.getSensorDName());
12136
12137        sensorSlipPanel.remove(slipSensorA.getDetailsPanel());
12138        sensorSlipPanel.remove(slipSensorB.getDetailsPanel());
12139        sensorSlipPanel.remove(slipSensorC.getDetailsPanel());
12140        sensorSlipPanel.remove(slipSensorD.getDetailsPanel());
12141
12142        slipSensorA.setBoundaryLabel(slipSensorBlocks[0]);
12143        slipSensorB.setBoundaryLabel(slipSensorBlocks[1]);
12144        slipSensorC.setBoundaryLabel(slipSensorBlocks[2]);
12145        slipSensorD.setBoundaryLabel(slipSensorBlocks[3]);
12146
12147        boolean boundaryFlag = false;
12148        if (slipSensorBlocks[0] != null) {
12149            sensorSlipPanel.add(slipSensorA.getDetailsPanel());
12150            boundaryFlag = true;
12151        }
12152        if (slipSensorBlocks[1] != null) {
12153            sensorSlipPanel.add(slipSensorB.getDetailsPanel());
12154            boundaryFlag = true;
12155        }
12156        if (slipSensorBlocks[2] != null) {
12157            sensorSlipPanel.add(slipSensorC.getDetailsPanel());
12158            boundaryFlag = true;
12159        }
12160        if (slipSensorBlocks[3] != null) {
12161            sensorSlipPanel.add(slipSensorD.getDetailsPanel());
12162            boundaryFlag = true;
12163        }
12164        if (!boundaryFlag) {
12165            JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame, Bundle.getMessage("NoBoundarySlipSensor"));
12166        }
12167        setSensorsAtSlipFrame.setPreferredSize(null);
12168        setSensorsAtSlipFrame.pack();
12169    }
12170
12171    private boolean getSlipSensorInformation() {
12172        if (!setSensorsAtSlipFromMenuFlag) {
12173            layoutSlip = null;
12174            List<LayoutSlip> layoutSlips = layoutEditor.getLayoutSlips();
12175            if (layoutSlips.size() <= 0) {
12176                JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame,
12177                        Bundle.getMessage("SignalsError15"),
12178                        Bundle.getMessage("ErrorTitle"),
12179                        JmriJOptionPane.ERROR_MESSAGE);
12180                return false;
12181            } else if (layoutSlips.size() == 1) {
12182                layoutSlip = layoutSlips.get(0);
12183            } else {
12184                LayoutBlock slipSensorBlockA = null;
12185                slipSensorBlockA = getBlockFromEntry(slipSensorsBlockAComboBox);
12186                if (slipSensorBlockA == null) {
12187                    return false;
12188                }
12189
12190                int foundCount = 0;
12191                for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
12192                    LayoutBlock xA = null;
12193                    LayoutBlock xB = null;
12194                    LayoutBlock xC = null;
12195                    LayoutBlock xD = null;
12196
12197                    LayoutBlock xAC = x.getLayoutBlock();
12198                    if (x.getConnectA() != null) {
12199                        xA = ((TrackSegment) x.getConnectA()).getLayoutBlock();
12200                    }
12201                    if (x.getConnectB() != null) {
12202                        xB = ((TrackSegment) x.getConnectB()).getLayoutBlock();
12203                    }
12204                    if (x.getConnectC() != null) {
12205                        xC = ((TrackSegment) x.getConnectC()).getLayoutBlock();
12206                    }
12207                    if (x.getConnectD() != null) {
12208                        xD = ((TrackSegment) x.getConnectD()).getLayoutBlock();
12209                    }
12210                    if (((xA != null) && (xC != null) && ((xA == slipSensorBlockA)
12211                            || (xC == slipSensorBlockA)))
12212                            || ((xB != null) && (xD != null) && (((xB == slipSensorBlockA))
12213                            || ((xD == slipSensorBlockA))))) {
12214                        layoutSlip = x;
12215                        foundCount++;
12216                    } else if ((xAC != null) && (xAC == slipSensorBlockA)) {
12217                        layoutSlip = x;
12218                        foundCount++;
12219                    }
12220                }
12221                if (foundCount == 0) {
12222                    //try one block test
12223                    for (LayoutSlip x : layoutEditor.getLayoutSlips()) {
12224                        if (slipSensorBlockA == x.getLayoutBlock()) {
12225                            layoutSlip = x;
12226                            foundCount++;
12227                        }
12228                    }
12229                }
12230                if (foundCount > 1) {
12231                    JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame,
12232                            Bundle.getMessage("SignalsError16",
12233                                    new Object[]{" " + foundCount + " "}),
12234                            Bundle.getMessage("ErrorTitle"),
12235                            JmriJOptionPane.ERROR_MESSAGE);
12236                    return false;
12237                }
12238                if (layoutSlip == null) {
12239                    JmriJOptionPane.showMessageDialog(setSensorsAtSlipFrame,
12240                            Bundle.getMessage("SignalsError17"),
12241                            Bundle.getMessage("ErrorTitle"),
12242                            JmriJOptionPane.ERROR_MESSAGE);
12243                    return false;
12244                }
12245            }
12246        }
12247        return true;
12248    }
12249
12250    private void setSlipSensorsCancelPressed(ActionEvent a) {
12251        setSensorsAtSlipOpenFlag = false;
12252        setSensorsAtSlipFrame.setVisible(false);
12253    }
12254
12255    private void setSlipSensorsDonePressed(ActionEvent a) {
12256        log.trace("setSlipSensorsDonePressed");
12257        if (!getSlipSensorInformation()) {
12258            return;
12259        }
12260
12261        Sensor sensorA = getSensorFromEntry(slipSensorA.getText(), false, setSensorsAtSlipFrame);
12262        Sensor sensorB = getSensorFromEntry(slipSensorB.getText(), false, setSensorsAtSlipFrame);
12263        Sensor sensorC = getSensorFromEntry(slipSensorC.getText(), false, setSensorsAtSlipFrame);
12264        Sensor sensorD = getSensorFromEntry(slipSensorD.getText(), false, setSensorsAtSlipFrame);
12265
12266        Sensor currSensorA = layoutSlip.getSensorA();
12267        Sensor currSensorB = layoutSlip.getSensorB();
12268        Sensor currSensorC = layoutSlip.getSensorC();
12269        Sensor currSensorD = layoutSlip.getSensorD();
12270
12271        if (log.isTraceEnabled()) {
12272            log.trace("current sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
12273                    (currSensorA == null) ? "- none- " : currSensorA.getDisplayName(), // NOI18N
12274                    (currSensorB == null) ? "- none- " : currSensorB.getDisplayName(), // NOI18N
12275                    (currSensorC == null) ? "- none- " : currSensorC.getDisplayName(), // NOI18N
12276                    (currSensorD == null) ? "- none- " : currSensorD.getDisplayName());  // NOI18N
12277            log.trace("new sensors: A = {}, B = {}, C = {}, D = {}", // NOI18N
12278                    (sensorA == null) ? "- none- " : sensorA.getDisplayName(), // NOI18N
12279                    (sensorB == null) ? "- none- " : sensorB.getDisplayName(), // NOI18N
12280                    (sensorC == null) ? "- none- " : sensorC.getDisplayName(), // NOI18N
12281                    (sensorD == null) ? "- none- " : sensorD.getDisplayName());  // NOI18N
12282        }
12283
12284        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
12285
12286        //place/remove sensors as requested
12287        if (sensorA == null) {
12288            if (currSensorA != null && removeSensorFromPanel(currSensorA)) {
12289                layoutSlip.setSensorA(null);
12290            }
12291        } else if (slipSensorA != null && layoutSlip.getConnectA() != null) {
12292            setTurnoutSensor(layoutSlip, sensorA, currSensorA, slipSensorA, layoutSlip.getConnectA(), layoutSlipView.getCoordsA(), "A");
12293        }
12294
12295        if (sensorB == null) {
12296            if (currSensorB != null && removeSensorFromPanel(currSensorB)) {
12297                layoutSlip.setSensorB(null);
12298            }
12299        } else if (slipSensorB != null && layoutSlip.getConnectB() != null) {
12300            setTurnoutSensor(layoutSlip, sensorB, currSensorB, slipSensorB, layoutSlip.getConnectB(), layoutSlipView.getCoordsB(), "B");
12301        }
12302
12303        if (sensorC == null) {
12304            if (currSensorC != null && removeSensorFromPanel(currSensorC)) {
12305                layoutSlip.setSensorC(null);
12306            }
12307        } else if (slipSensorC != null && layoutSlip.getConnectC() != null) {
12308            setTurnoutSensor(layoutSlip, sensorC, currSensorC, slipSensorC, layoutSlip.getConnectC(), layoutSlipView.getCoordsC(), "C");
12309        }
12310
12311        if (sensorD == null) {
12312            if (currSensorD != null && removeSensorFromPanel(currSensorD)) {
12313                layoutSlip.setSensorD(null);
12314            }
12315        } else if (slipSensorD != null && layoutSlip.getConnectD() != null) {
12316            setTurnoutSensor(layoutSlip, sensorD, currSensorD, slipSensorD, layoutSlip.getConnectD(), layoutSlipView.getCoordsD(), "D");
12317        }
12318
12319        //setup logic if requested
12320        //finish up
12321        setSensorsAtSlipOpenFlag = false;
12322        setSensorsAtSlipFrame.setVisible(false);
12323        if (needRedraw) {
12324            layoutEditor.redrawPanel();
12325            needRedraw = false;
12326            layoutEditor.setDirty();
12327
12328        }
12329    }
12330
12331    static class BeanDetails<B extends NamedBean> {
12332
12333        private final String bundleName;
12334        private final String beanString;
12335        private final JLabel textLabel;
12336
12337        private final String boundaryLabelText = Bundle.getMessage("BoundaryOf");
12338        private final JLabel boundaryLabel = new JLabel(boundaryLabelText);
12339
12340        private final Manager<B> manager;
12341
12342        private final JPanel detailsPanel = new JPanel(new FlowLayout());
12343        private final JRadioButton addBeanCheck = new JRadioButton(Bundle.getMessage("DoNotPlace"));
12344        private final JRadioButton left = new JRadioButton(Bundle.getMessage("LeftHandSide"));
12345        private final JRadioButton right = new JRadioButton(Bundle.getMessage("RightHandSide"));
12346        private final ButtonGroup buttonGroup = new ButtonGroup();
12347        private final NamedBeanComboBox<B> beanCombo;
12348
12349        private final JLabel boundaryBlocks = new JLabel();
12350
12351        private final Border blackline = BorderFactory.createLineBorder(Color.black);
12352
12353        BeanDetails(@Nonnull String beanType, @Nonnull Manager<B> manager) {
12354            beanCombo = new NamedBeanComboBox<>(manager);
12355            beanCombo.setAllowNull(true);
12356            JComboBoxUtil.setupComboBoxMaxRows(beanCombo);
12357            //I18N translate from type (Sensor) to BeanNameSensor
12358            //to use NamedBeanBundle property
12359            if ("Sensor".equals(beanType)) {
12360                bundleName = "BeanNameSensor";
12361            } else if ("SignalMast".equals(beanType)) {
12362                bundleName = "BeanNameSignalMast";
12363            } else {
12364                log.error("Unexpected value for BeanDetails: '{}'", beanType);
12365                bundleName = beanType;
12366            }
12367            beanString = Bundle.getMessage(bundleName);
12368            textLabel = new JLabel(beanString);
12369            this.manager = manager;
12370            //this.beanType = beanType;
12371
12372            buttonGroup.add(addBeanCheck);
12373            buttonGroup.add(left);
12374            buttonGroup.add(right);
12375            addBeanCheck.setSelected(true);
12376
12377            boundaryBlocks.setAlignmentX(Component.CENTER_ALIGNMENT);
12378            boundaryBlocks.setOpaque(false);
12379            detailsPanel.setLayout(new BorderLayout());
12380            detailsPanel.setBorder(BorderFactory.createTitledBorder(blackline, Bundle.getMessage("BlockBoundary")));
12381            boundaryLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
12382
12383            JPanel boundaryDetails = new JPanel(new FlowLayout());
12384            boundaryDetails.setOpaque(false);
12385            boundaryDetails.setLayout(new BoxLayout(boundaryDetails, BoxLayout.Y_AXIS));
12386            boundaryDetails.add(boundaryLabel);
12387            boundaryDetails.add(boundaryBlocks);
12388
12389            detailsPanel.add(boundaryDetails, BorderLayout.PAGE_START);
12390            detailsPanel.add(addIconPanel(), BorderLayout.CENTER);
12391            detailsPanel.add(positionLeftRight(), BorderLayout.PAGE_END);
12392        }
12393
12394        protected void setTextField(String value) {
12395            beanCombo.setSelectedItem(manager.getNamedBean(value));
12396        }
12397
12398        protected String getText() {
12399            return beanCombo.getSelectedItemDisplayName();
12400        }
12401
12402        protected B getBean() {
12403            return beanCombo.getSelectedItem();
12404        }
12405
12406        protected JPanel getDetailsPanel() {
12407            return detailsPanel;
12408        }
12409
12410        protected boolean addToPanel() {
12411            return !addBeanCheck.isSelected();
12412        }
12413
12414        protected boolean isRightSelected() {
12415            return right.isSelected();
12416        }
12417
12418        protected void setBoundaryTitle(String text) {
12419            detailsPanel.setBorder(BorderFactory.createTitledBorder(blackline, text));
12420        }
12421
12422        protected void setBoundaryLabelText(String text) {
12423            boundaryLabel.setText(text);
12424        }
12425
12426        protected void setBoundaryLabel(String label) {
12427            boundaryBlocks.setText(label);
12428        }
12429
12430        protected NamedBeanComboBox<B> getCombo() {
12431            return beanCombo;
12432        }
12433
12434        protected JPanel positionLeftRight() {
12435            JPanel placementPanel = new JPanel();
12436            placementPanel.setBorder(BorderFactory.createTitledBorder(
12437                    blackline,
12438                    Bundle.getMessage("PlaceItem", new Object[]{beanString})));
12439
12440            placementPanel.setLayout(new BoxLayout(placementPanel, BoxLayout.Y_AXIS));
12441            placementPanel.setOpaque(false);
12442            placementPanel.add(addBeanCheck);
12443            placementPanel.add(left);
12444            placementPanel.add(right);
12445            addBeanCheck.setOpaque(false);
12446            left.setOpaque(false);
12447            right.setOpaque(false);
12448
12449            addBeanCheck.setToolTipText(Bundle.getMessage("PlaceItemToolTip",
12450                    new Object[]{beanString}));
12451
12452            right.setToolTipText(Bundle.getMessage("PlaceRightToolTip",
12453                    new Object[]{beanString}));
12454
12455            left.setToolTipText(Bundle.getMessage("PlaceLeftToolTip",
12456                    new Object[]{beanString}));
12457            return placementPanel;
12458        }
12459
12460        protected JPanel addIconPanel() {
12461            JPanel addBeanPanel = new JPanel(new FlowLayout());
12462            addBeanPanel.setOpaque(false);
12463            addBeanPanel.add(textLabel);
12464            textLabel.setOpaque(false);
12465            addBeanPanel.add(beanCombo);
12466            return addBeanPanel;
12467        }
12468    }
12469
12470    /*==================*\
12471    |* setSignalsAtSlip *|
12472    \*==================*/
12473//operational variables for Set Signals at slip tool
12474    private JmriJFrame setSignalsAtSlipFrame = null;
12475    private boolean setSignalsAtSlipOpenFlag = false;
12476    private boolean setSignalsAtSlipFromMenuFlag = false;
12477
12478    private final JComboBox<String> slipNameComboBox = new JComboBox<>();
12479
12480    private final NamedBeanComboBox<SignalHead> a1SlipSignalHeadComboBox
12481            = new NamedBeanComboBox<>(
12482                    InstanceManager.getDefault(SignalHeadManager.class),
12483                    null, DisplayOptions.DISPLAYNAME);
12484    private final NamedBeanComboBox<SignalHead> a2SlipSignalHeadComboBox
12485            = new NamedBeanComboBox<>(
12486                    InstanceManager.getDefault(SignalHeadManager.class),
12487                    null, DisplayOptions.DISPLAYNAME);
12488    private final NamedBeanComboBox<SignalHead> b1SlipSignalHeadComboBox
12489            = new NamedBeanComboBox<>(
12490                    InstanceManager.getDefault(SignalHeadManager.class),
12491                    null, DisplayOptions.DISPLAYNAME);
12492    private final NamedBeanComboBox<SignalHead> b2SlipSignalHeadComboBox
12493            = new NamedBeanComboBox<>(
12494                    InstanceManager.getDefault(SignalHeadManager.class),
12495                    null, DisplayOptions.DISPLAYNAME);
12496    private final NamedBeanComboBox<SignalHead> c1SlipSignalHeadComboBox
12497            = new NamedBeanComboBox<>(
12498                    InstanceManager.getDefault(SignalHeadManager.class),
12499                    null, DisplayOptions.DISPLAYNAME);
12500    private final NamedBeanComboBox<SignalHead> c2SlipSignalHeadComboBox
12501            = new NamedBeanComboBox<>(
12502                    InstanceManager.getDefault(SignalHeadManager.class),
12503                    null, DisplayOptions.DISPLAYNAME);
12504    private final NamedBeanComboBox<SignalHead> d1SlipSignalHeadComboBox
12505            = new NamedBeanComboBox<>(
12506                    InstanceManager.getDefault(SignalHeadManager.class),
12507                    null, DisplayOptions.DISPLAYNAME);
12508    private final NamedBeanComboBox<SignalHead> d2SlipSignalHeadComboBox
12509            = new NamedBeanComboBox<>(
12510                    InstanceManager.getDefault(SignalHeadManager.class),
12511                    null, DisplayOptions.DISPLAYNAME);
12512
12513    private final JCheckBox setA1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12514    private final JCheckBox setupA1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12515    private final JCheckBox setA2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12516    private final JCheckBox setupA2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12517    private final JCheckBox setB1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12518    private final JCheckBox setupB1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12519    private final JCheckBox setB2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12520    private final JCheckBox setupB2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12521    private final JCheckBox setC1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12522    private final JCheckBox setupC1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12523    private final JCheckBox setC2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12524    private final JCheckBox setupC2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12525    private final JCheckBox setD1SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12526    private final JCheckBox setupD1SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12527    private final JCheckBox setD2SlipHead = new JCheckBox(Bundle.getMessage("PlaceHead"));
12528    private final JCheckBox setupD2SlipLogic = new JCheckBox(Bundle.getMessage("SetLogic"));
12529
12530    private JButton setSlipSignalsDone = null;
12531    private JButton setSlipSignalsCancel = null;
12532    private LayoutSlip layoutSlip = null;
12533
12534    private SignalHead a1SlipHead = null;
12535    private SignalHead a2SlipHead = null;
12536    private SignalHead b1SlipHead = null;
12537    private SignalHead b2SlipHead = null;
12538    private SignalHead c1SlipHead = null;
12539    private SignalHead c2SlipHead = null;
12540    private SignalHead d1SlipHead = null;
12541    private SignalHead d2SlipHead = null;
12542
12543    private JPanel dblSlipC2SigPanel;
12544    private JPanel dblSlipB2SigPanel;
12545
12546    public void setSignalsAtSlipFromMenu(@Nonnull LayoutSlip ls,
12547            @Nonnull MultiIconEditor theEditor, @Nonnull JFrame theFrame) {
12548        layoutSlip = ls;
12549        a1SlipSignalHeadComboBox.setSelectedItem(null);
12550        a2SlipSignalHeadComboBox.setSelectedItem(null);
12551        b1SlipSignalHeadComboBox.setSelectedItem(null);
12552        b2SlipSignalHeadComboBox.setSelectedItem(null);
12553        c1SlipSignalHeadComboBox.setSelectedItem(null);
12554        c2SlipSignalHeadComboBox.setSelectedItem(null);
12555        d1SlipSignalHeadComboBox.setSelectedItem(null);
12556        d2SlipSignalHeadComboBox.setSelectedItem(null);
12557
12558        setSignalsAtSlipFromMenuFlag = true;
12559        setSignalsAtSlip(theEditor, theFrame);
12560        setSignalsAtSlipFromMenuFlag = false;
12561    }
12562
12563    public void setSignalsAtSlip(@Nonnull MultiIconEditor theEditor,
12564            @Nonnull JFrame theFrame) {
12565        signalIconEditor = theEditor;
12566        signalFrame = theFrame;
12567
12568        //Initialize if needed
12569        if (setSignalsAtSlipFrame == null) {
12570            setSignalsAtSlipOpenFlag = false;
12571            setSignalsAtSlipFrame = new JmriJFrame(Bundle.getMessage("SignalsAtSlip"), false, true);
12572            oneFrameToRuleThemAll(setSignalsAtSlipFrame);
12573            setSignalsAtSlipFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
12574            setSignalsAtSlipFrame.addHelpMenu("package.jmri.jmrit.display.SetSignalsAtSlip", true);
12575            setSignalsAtSlipFrame.setLocation(70, 30);
12576            Container theContentPane = setSignalsAtSlipFrame.getContentPane();
12577            theContentPane.setLayout(new BoxLayout(theContentPane, BoxLayout.Y_AXIS));
12578
12579            JPanel panel1 = new JPanel(new FlowLayout());
12580            JLabel turnout1NameLabel = new JLabel(Bundle.getMessage("Slip") + " "
12581                    + Bundle.getMessage("Name"));
12582            panel1.add(turnout1NameLabel);
12583            panel1.add(slipNameComboBox);
12584            for (LayoutSlip slip : layoutEditor.getLayoutSlips()) {
12585                slipNameComboBox.addItem(slip.getDisplayName());
12586            }
12587
12588            slipNameComboBox.insertItemAt("", 0);
12589
12590            if (layoutSlip != null) {
12591                slipNameComboBox.setSelectedItem(layoutSlip.getDisplayName());
12592                getSlipTurnoutSignalsGetSaved(null);
12593            } else {
12594                slipNameComboBox.setSelectedIndex(0);
12595            }
12596            slipNameComboBox.addActionListener((ActionEvent e) -> {
12597                for (LayoutSlip slip : layoutEditor.getLayoutSlips()) {
12598                    if (slip.getDisplayName().equals(slipNameComboBox.getSelectedItem())) {
12599                        //slip1NameField.setText(slip.getDisplayName());
12600                        getSlipTurnoutSignalsGetSaved(e);
12601                        boolean enable = (slip.getSlipType() == LayoutSlip.TurnoutType.DOUBLE_SLIP);
12602                        dblSlipC2SigPanel.setVisible(enable);
12603                        dblSlipB2SigPanel.setVisible(enable);
12604                        setSignalsAtSlipFrame.pack();
12605                        return;
12606                    }
12607                }
12608            });
12609            theContentPane.add(panel1);
12610            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12611
12612            JPanel panel2a = new JPanel(new FlowLayout());
12613            panel2a.add(new JLabel("   "));
12614            panel2a.add(setPlaceAllHeads);
12615            setPlaceAllHeads.setToolTipText(Bundle.getMessage("PlaceAllHeadsHint"));
12616            setPlaceAllHeads.addActionListener((ActionEvent e) -> {
12617                boolean isSelected = setPlaceAllHeads.isSelected();
12618                //(de)select all checkboxes
12619                setA1SlipHead.setSelected(isSelected);
12620                setA2SlipHead.setSelected(isSelected);
12621                setB1SlipHead.setSelected(isSelected);
12622                setB2SlipHead.setSelected(isSelected);
12623                setC1SlipHead.setSelected(isSelected);
12624                setC2SlipHead.setSelected(isSelected);
12625                setD1SlipHead.setSelected(isSelected);
12626                setD2SlipHead.setSelected(isSelected);
12627            });
12628            panel2a.add(new JLabel("  "));
12629            panel2a.add(setupAllLogic);
12630            setupAllLogic.setToolTipText(Bundle.getMessage("SetAllLogicHint"));
12631            setupAllLogic.addActionListener((ActionEvent e) -> {
12632                boolean isSelected = setupAllLogic.isSelected();
12633                //(de)select all checkboxes
12634                setupA1SlipLogic.setSelected(isSelected);
12635                setupA2SlipLogic.setSelected(isSelected);
12636                setupB1SlipLogic.setSelected(isSelected);
12637                setupB2SlipLogic.setSelected(isSelected);
12638                setupC1SlipLogic.setSelected(isSelected);
12639                setupC2SlipLogic.setSelected(isSelected);
12640                setupD1SlipLogic.setSelected(isSelected);
12641                setupD2SlipLogic.setSelected(isSelected);
12642            });
12643            theContentPane.add(panel2a);
12644            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12645
12646            //Signal heads located at turnout 1
12647            JPanel panel21x = new JPanel(new FlowLayout());
12648            panel21x.add(new JLabel(Bundle.getMessage("SignalLocated")
12649                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
12650                    + Bundle.getMessage("ContinuingTrack")));
12651            theContentPane.add(panel21x);
12652
12653            JPanel panel21 = new JPanel(new FlowLayout());
12654            panel21.add(new JLabel(Bundle.getMessage("MakeLabel",
12655                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12656                    + Bundle.getMessage("ContinuingTrack"))));
12657            panel21.add(a1SlipSignalHeadComboBox);
12658            theContentPane.add(panel21);
12659            a1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12660
12661            JPanel panel22 = new JPanel(new FlowLayout());
12662            panel22.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
12663            panel22.add(setA1SlipHead);
12664            setA1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12665            panel22.add(new JLabel("  "));
12666            panel22.add(setupA1SlipLogic);
12667            setupA1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12668            theContentPane.add(panel22);
12669
12670            JPanel panel23 = new JPanel(new FlowLayout());
12671            panel23.add(new JLabel(Bundle.getMessage("MakeLabel",
12672                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12673                    + Bundle.getMessage("DivergingTrack"))));
12674            panel23.add(a2SlipSignalHeadComboBox);
12675            theContentPane.add(panel23);
12676            a2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12677
12678            JPanel panel24 = new JPanel(new FlowLayout());
12679            panel24.add(new JLabel("   "));
12680            panel24.add(setA2SlipHead);
12681            setA2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12682            panel24.add(new JLabel("  "));
12683            panel24.add(setupA2SlipLogic);
12684            setupA2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12685            theContentPane.add(panel24);
12686
12687            JPanel panel31x = new JPanel(new FlowLayout());
12688            panel31x.add(new JLabel(Bundle.getMessage("SignalLocated")
12689                    + " " + Bundle.getMessage("BeanNameTurnout") + " 1 - "
12690                    + Bundle.getMessage("DivergingTrack")));
12691            theContentPane.add(panel31x);
12692
12693            JPanel panel31 = new JPanel(new FlowLayout());
12694            panel31.add(new JLabel(Bundle.getMessage("MakeLabel",
12695                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12696                    + Bundle.getMessage("ContinuingTrack"))));
12697            panel31.add(b1SlipSignalHeadComboBox);
12698            theContentPane.add(panel31);
12699            b1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12700
12701            JPanel panel32 = new JPanel(new FlowLayout());
12702            panel32.add(new JLabel(Bundle.getMessage("OrBoth") + " 2 " + Bundle.getMessage("Tracks)") + "   "));
12703            panel32.add(setB1SlipHead);
12704            setB1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12705            panel32.add(new JLabel("  "));
12706            panel32.add(setupB1SlipLogic);
12707            setupB1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12708            theContentPane.add(panel32);
12709
12710            dblSlipB2SigPanel = new JPanel(new FlowLayout());
12711            dblSlipB2SigPanel.setLayout(new BoxLayout(dblSlipB2SigPanel, BoxLayout.Y_AXIS));
12712
12713            JPanel panel33 = new JPanel(new FlowLayout());
12714            panel33.add(new JLabel(Bundle.getMessage("MakeLabel",
12715                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12716                    + Bundle.getMessage("DivergingTrack"))));
12717            panel33.add(b2SlipSignalHeadComboBox);
12718            dblSlipB2SigPanel.add(panel33);
12719            b2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12720
12721            JPanel panel34 = new JPanel(new FlowLayout());
12722            panel34.add(new JLabel("   "));
12723            panel34.add(setB2SlipHead);
12724            setB2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12725            panel34.add(new JLabel("  "));
12726            panel34.add(setupB2SlipLogic);
12727            setupB2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12728            dblSlipB2SigPanel.add(panel34);
12729
12730            theContentPane.add(dblSlipB2SigPanel);
12731            dblSlipB2SigPanel.setVisible(false);
12732            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12733            //Signal heads located at turnout 2
12734
12735            JPanel panel41x = new JPanel(new FlowLayout());
12736            panel41x.add(new JLabel(Bundle.getMessage("SignalLocated")
12737                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
12738                    + Bundle.getMessage("ContinuingTrack")));
12739            theContentPane.add(panel41x);
12740
12741            JPanel panel41 = new JPanel(new FlowLayout());
12742            panel41.add(new JLabel(Bundle.getMessage("MakeLabel",
12743                    Bundle.getMessage("ProtectsTurnout") + " 2 - "
12744                    + Bundle.getMessage("ContinuingTrack"))));
12745            panel41.add(c1SlipSignalHeadComboBox);
12746            theContentPane.add(panel41);
12747            c1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12748
12749            JPanel panel42 = new JPanel(new FlowLayout());
12750            panel42.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
12751            panel42.add(setC1SlipHead);
12752            setC1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12753            panel42.add(new JLabel("  "));
12754            panel42.add(setupC1SlipLogic);
12755            setupC1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12756            theContentPane.add(panel42);
12757            dblSlipC2SigPanel = new JPanel(new FlowLayout());
12758            dblSlipC2SigPanel.setLayout(new BoxLayout(dblSlipC2SigPanel, BoxLayout.Y_AXIS));
12759
12760            JPanel panel43 = new JPanel(new FlowLayout());
12761            panel43.add(new JLabel(Bundle.getMessage("MakeLabel",
12762                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
12763                    + Bundle.getMessage("DivergingTrack"))));
12764            panel43.add(c2SlipSignalHeadComboBox);
12765            dblSlipC2SigPanel.add(panel43);
12766            c2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12767
12768            JPanel panel44 = new JPanel(new FlowLayout());
12769            panel44.add(new JLabel("   "));
12770            panel44.add(setC2SlipHead);
12771            setC2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12772            panel44.add(new JLabel("  "));
12773            panel44.add(setupC2SlipLogic);
12774            setupC2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12775            dblSlipC2SigPanel.add(panel44);
12776            theContentPane.add(dblSlipC2SigPanel);
12777
12778            JPanel panel51x = new JPanel(new FlowLayout());
12779            panel51x.add(new JLabel(Bundle.getMessage("SignalLocated")
12780                    + " " + Bundle.getMessage("BeanNameTurnout") + " 2 - "
12781                    + Bundle.getMessage("DivergingTrack")));
12782            theContentPane.add(panel51x);
12783
12784            JPanel panel51 = new JPanel(new FlowLayout());
12785            panel51.add(new JLabel(Bundle.getMessage("MakeLabel",
12786                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
12787                    + Bundle.getMessage("ContinuingTrack"))));
12788            panel51.add(d1SlipSignalHeadComboBox);
12789            theContentPane.add(panel51);
12790            d1SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12791
12792            JPanel panel52 = new JPanel(new FlowLayout());
12793            panel52.add(new JLabel(Bundle.getMessage("OrBoth") + " 1 " + Bundle.getMessage("Tracks)") + "   "));
12794            panel52.add(setD1SlipHead);
12795            setD1SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12796            panel52.add(new JLabel("  "));
12797            panel52.add(setupD1SlipLogic);
12798            setupD1SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12799            theContentPane.add(panel52);
12800
12801            JPanel panel53 = new JPanel(new FlowLayout());
12802            panel53.add(new JLabel(Bundle.getMessage("MakeLabel",
12803                    Bundle.getMessage("ProtectsTurnout") + " 1 - "
12804                    + Bundle.getMessage("DivergingTrack"))));
12805            panel53.add(d2SlipSignalHeadComboBox);
12806            theContentPane.add(panel53);
12807            d2SlipSignalHeadComboBox.setToolTipText(Bundle.getMessage("SignalHeadNameHint"));
12808
12809            JPanel panel54 = new JPanel(new FlowLayout());
12810            panel54.add(new JLabel("   "));
12811            panel54.add(setD2SlipHead);
12812            setD2SlipHead.setToolTipText(Bundle.getMessage("PlaceHeadHint"));
12813            panel54.add(new JLabel("  "));
12814            panel54.add(setupD2SlipLogic);
12815            setupD2SlipLogic.setToolTipText(Bundle.getMessage("SetLogicHint"));
12816            theContentPane.add(panel54);
12817            theContentPane.add(new JSeparator(JSeparator.HORIZONTAL));
12818
12819            JPanel panel6 = new JPanel(new FlowLayout());
12820            panel6.add(changeTToTSignalIcon = new JButton(Bundle.getMessage("ChangeSignalIcon")));
12821            changeTToTSignalIcon.addActionListener((ActionEvent e) -> signalFrame.setVisible(true));
12822            changeTToTSignalIcon.setToolTipText(Bundle.getMessage("ChangeSignalIconHint"));
12823            panel6.add(new JLabel("   "));
12824            panel6.add(setSlipSignalsDone = new JButton(Bundle.getMessage("ButtonDone")));
12825            setSlipSignalsDone.addActionListener(this::setSlipSignalsDonePressed);
12826            setSlipSignalsDone.setToolTipText(Bundle.getMessage("DoneHint", Bundle.getMessage("ButtonDone")));
12827            panel6.add(setSlipSignalsCancel = new JButton(Bundle.getMessage("ButtonCancel")));
12828            setSlipSignalsCancel.addActionListener(this::setSlipSignalsCancelPressed);
12829            setSlipSignalsCancel.setToolTipText(Bundle.getMessage("CancelHint", Bundle.getMessage("ButtonCancel")));
12830            theContentPane.add(panel6);
12831            setSignalsAtSlipFrame.addWindowListener(new WindowAdapter() {
12832                @Override
12833                public void windowClosing(WindowEvent e) {
12834                    setSlipSignalsCancelPressed(null);
12835                }
12836            });
12837        }
12838        setPlaceAllHeads.setSelected(false);
12839        setupAllLogic.setSelected(false);
12840
12841        boolean enable = (layoutSlip != null
12842                && layoutSlip.getSlipType() == LayoutSlip.TurnoutType.DOUBLE_SLIP);
12843        dblSlipC2SigPanel.setVisible(enable);
12844        dblSlipB2SigPanel.setVisible(enable);
12845
12846        if (setSignalsAtSlipFromMenuFlag) {
12847            getSlipTurnoutSignalsGetSaved(null);
12848        }
12849
12850        if (!setSignalsAtSlipOpenFlag) {
12851            setSignalsAtSlipFrame.setPreferredSize(null);
12852            setSignalsAtSlipFrame.pack();
12853            setSignalsAtSlipOpenFlag = true;
12854        }
12855        setSignalsAtSlipFrame.setVisible(true);
12856    }
12857
12858    private void getSlipTurnoutSignalsGetSaved(ActionEvent a) {
12859        if (!getSlipTurnoutInformation()) {
12860            return;
12861        }
12862        a1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalA1());
12863        a2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalA2());
12864        b1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalB1());
12865        b2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalB2());
12866        c1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalC1());
12867        c2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalC2());
12868        d1SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalD1());
12869        d2SlipSignalHeadComboBox.setSelectedItem(layoutSlip.getSignalD2());
12870    }
12871
12872    private void setSlipSignalsCancelPressed(ActionEvent a) {
12873        setSignalsAtSlipOpenFlag = false;
12874        setSignalsAtSlipFrame.setVisible(false);
12875    }
12876
12877    private boolean getSlipTurnoutInformation() {
12878        turnout1 = null;
12879        turnout2 = null;
12880        layoutSlip = null;
12881        for (LayoutSlip ls : layoutEditor.getLayoutSlips()) {
12882            if (ls.getDisplayName().equals(slipNameComboBox.getSelectedItem())) {
12883                layoutSlip = ls;
12884                break;
12885            }
12886        }
12887        if (layoutSlip == null) {
12888            return false;
12889        }
12890        String str = layoutSlip.getDisplayName();
12891
12892        turnout1 = layoutSlip.getTurnout();
12893        if (turnout1 == null) {
12894            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12895                    Bundle.getMessage("SignalsError2",
12896                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
12897                    JmriJOptionPane.ERROR_MESSAGE);
12898            return false;
12899        }
12900        turnout2 = layoutSlip.getTurnoutB();
12901        if (turnout2 == null) {
12902            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12903                    Bundle.getMessage("SignalsError2",
12904                            new Object[]{str}), Bundle.getMessage("ErrorTitle"),
12905                    JmriJOptionPane.ERROR_MESSAGE);
12906            return false;
12907        }
12908        return true;
12909    }
12910
12911    private void setSlipSignalsDonePressed(ActionEvent a) {
12912        if (!getSlipTurnoutInformation()) {
12913            return;
12914        }
12915        if (!getSlipSignalHeadInformation()) {
12916            return;
12917        }
12918
12919        //place signal icons if requested, and assign signal heads to this turnout
12920        String signalHeadName = a1SlipSignalHeadComboBox.getSelectedItemDisplayName();
12921        if (signalHeadName == null) {
12922            signalHeadName = "";
12923        }
12924        if (setA1SlipHead.isSelected()) {
12925            if (isHeadOnPanel(a1SlipHead)
12926                    && (a1SlipHead != getHeadFromName(layoutSlip.getSignalB1Name()))) {
12927                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12928                        Bundle.getMessage("SignalsError6",
12929                                new Object[]{signalHeadName}),
12930                        Bundle.getMessage("ErrorTitle"),
12931                        JmriJOptionPane.ERROR_MESSAGE);
12932                return;
12933            } else {
12934                removeSignalHeadFromPanel(layoutSlip.getSignalA1Name());
12935                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
12936                    placeA1Slip(signalHeadName);
12937                } else {
12938                    placeB1Slip(signalHeadName);
12939                }
12940                removeAssignment(a1SlipHead);
12941                layoutSlip.setSignalA1Name(signalHeadName);
12942                needRedraw = true;
12943            }
12944        } else {
12945            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a1SlipHead, layoutSlip);
12946            if (assigned == LayoutTurnout.Geometry.NONE) {
12947                if (isHeadOnPanel(a1SlipHead)
12948                        && isHeadAssignedAnywhere(a1SlipHead)) {
12949                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12950                            Bundle.getMessage("SignalsError8",
12951                                    new Object[]{signalHeadName}),
12952                            Bundle.getMessage("ErrorTitle"),
12953                            JmriJOptionPane.ERROR_MESSAGE);
12954                    return;
12955                } else {
12956                    removeSignalHeadFromPanel(layoutSlip.getSignalB1Name());
12957                    removeAssignment(a1SlipHead);
12958                    layoutSlip.setSignalA1Name(signalHeadName);
12959                }
12960                //} else if (assigned != B1) {
12961                //need to figure out what to do in this case - assigned to a different position on the same turnout.
12962            }
12963        }
12964
12965        signalHeadName = a2SlipSignalHeadComboBox.getSelectedItemDisplayName();
12966        if (signalHeadName == null) {
12967            signalHeadName = "";
12968        }
12969        if ((a2SlipHead != null) && setA2SlipHead.isSelected()) {
12970            if (isHeadOnPanel(a2SlipHead)
12971                    && (a2SlipHead != getHeadFromName(layoutSlip.getSignalB2Name()))) {
12972                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12973                        Bundle.getMessage("SignalsError6",
12974                                new Object[]{signalHeadName}),
12975                        Bundle.getMessage("ErrorTitle"),
12976                        JmriJOptionPane.ERROR_MESSAGE);
12977                return;
12978            } else {
12979                removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
12980                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
12981                    placeA2Slip(signalHeadName);
12982                } else {
12983                    placeB2Slip(signalHeadName);
12984                }
12985                removeAssignment(a2SlipHead);
12986                layoutSlip.setSignalA2Name(signalHeadName);
12987                needRedraw = true;
12988            }
12989        } else if (a2SlipHead != null) {
12990            LayoutTurnout.Geometry assigned = isHeadAssignedHere(a2SlipHead, layoutSlip);
12991            if (assigned == LayoutTurnout.Geometry.NONE) {
12992                if (isHeadOnPanel(a2SlipHead)
12993                        && isHeadAssignedAnywhere(a2SlipHead)) {
12994                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
12995                            Bundle.getMessage("SignalsError8",
12996                                    new Object[]{signalHeadName}),
12997                            Bundle.getMessage("ErrorTitle"),
12998                            JmriJOptionPane.ERROR_MESSAGE);
12999                    return;
13000                } else {
13001                    removeSignalHeadFromPanel(layoutSlip.getSignalA2Name());
13002                    removeAssignment(a2SlipHead);
13003                    layoutSlip.setSignalA2Name(signalHeadName);
13004                }
13005                //} else if (assigned != B2) {
13006                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13007            }
13008        } else { //a2SlipHead known to be null here
13009            removeSignalHeadFromPanel(layoutSlip.getSignalA2Name());
13010            layoutSlip.setSignalB2Name("");
13011        }
13012
13013        signalHeadName = b1SlipSignalHeadComboBox.getSelectedItemDisplayName();
13014        if (signalHeadName == null) {
13015            signalHeadName = "";
13016        }
13017        if (setB1SlipHead.isSelected()) {
13018            if (isHeadOnPanel(b1SlipHead)
13019                    && (b1SlipHead != getHeadFromName(layoutSlip.getSignalC1Name()))) {
13020                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13021                        Bundle.getMessage("SignalsError6",
13022                                new Object[]{signalHeadName}),
13023                        Bundle.getMessage("ErrorTitle"),
13024                        JmriJOptionPane.ERROR_MESSAGE);
13025                return;
13026            } else {
13027                removeSignalHeadFromPanel(layoutSlip.getSignalB1Name());
13028                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13029                    placeB1Slip(signalHeadName);
13030                } else {
13031                    placeA1Slip(signalHeadName);
13032                }
13033                removeAssignment(b1SlipHead);
13034                layoutSlip.setSignalB1Name(signalHeadName);
13035                needRedraw = true;
13036            }
13037        } else {
13038            LayoutTurnout.Geometry assigned = isHeadAssignedHere(b1SlipHead, layoutSlip);
13039            if (assigned == LayoutTurnout.Geometry.NONE) {
13040                if (isHeadOnPanel(b1SlipHead)
13041                        && isHeadAssignedAnywhere(b1SlipHead)) {
13042                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13043                            Bundle.getMessage("SignalsError8",
13044                                    new Object[]{signalHeadName}),
13045                            Bundle.getMessage("ErrorTitle"),
13046                            JmriJOptionPane.ERROR_MESSAGE);
13047                    return;
13048                } else {
13049                    removeSignalHeadFromPanel(layoutSlip.getSignalB1Name());
13050                    removeAssignment(b1SlipHead);
13051                    layoutSlip.setSignalB1Name(signalHeadName);
13052                }
13053                //} else if (assigned != C1) {
13054                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13055            }
13056        }
13057
13058        if (layoutSlip.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
13059            signalHeadName = b2SlipSignalHeadComboBox.getSelectedItemDisplayName();
13060            if (signalHeadName == null) {
13061                signalHeadName = "";
13062            }
13063            if ((b2SlipHead != null) && setB2SlipHead.isSelected()) {
13064                if (isHeadOnPanel(b2SlipHead)
13065                        && (b2SlipHead != getHeadFromName(layoutSlip.getSignalC2Name()))) {
13066                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13067                            Bundle.getMessage("SignalsError6",
13068                                    new Object[]{signalHeadName}),
13069                            Bundle.getMessage("ErrorTitle"),
13070                            JmriJOptionPane.ERROR_MESSAGE);
13071                    return;
13072                } else {
13073                    removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13074                    if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13075                        placeB2Slip(signalHeadName);
13076                    } else {
13077                        placeA2Slip(signalHeadName);
13078                    }
13079                    removeAssignment(b2SlipHead);
13080                    layoutSlip.setSignalB2Name(signalHeadName);
13081                    needRedraw = true;
13082                }
13083            } else if (b2SlipHead != null) {
13084                LayoutTurnout.Geometry assigned = isHeadAssignedHere(b2SlipHead, layoutSlip);
13085                if (assigned == LayoutTurnout.Geometry.NONE) {
13086                    if (isHeadOnPanel(b2SlipHead)
13087                            && isHeadAssignedAnywhere(b2SlipHead)) {
13088                        JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13089                                Bundle.getMessage("SignalsError8",
13090                                        new Object[]{signalHeadName}),
13091                                Bundle.getMessage("ErrorTitle"),
13092                                JmriJOptionPane.ERROR_MESSAGE);
13093                        return;
13094                    } else {
13095                        removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13096                        removeAssignment(b2SlipHead);
13097                        layoutSlip.setSignalB2Name(signalHeadName);
13098                    }
13099                    //} else if (assigned != C2) {
13100                    //need to figure out what to do in this case - assigned to a different position on the same turnout.
13101                }
13102            } else { //b2SlipHead known to be null here
13103                removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13104                layoutSlip.setSignalB2Name("");
13105            }
13106        } else {
13107            if (b2SlipHead != null) {
13108                BlockBossLogic.getStoppedObject(layoutSlip.getSignalB2Name());
13109                removeSignalHeadFromPanel(layoutSlip.getSignalB2Name());
13110                layoutSlip.setSignalB2Name("");
13111                b2SlipHead = null;
13112            }
13113        }
13114
13115        //signal heads on turnout 2
13116        signalHeadName = c1SlipSignalHeadComboBox.getSelectedItemDisplayName();
13117        if (signalHeadName == null) {
13118            signalHeadName = "";
13119        }
13120        if (setC1SlipHead.isSelected()) {
13121            if (isHeadOnPanel(c1SlipHead)
13122                    && (c1SlipHead != getHeadFromName(layoutSlip.getSignalB1Name()))) {
13123                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13124                        Bundle.getMessage("SignalsError6",
13125                                new Object[]{signalHeadName}),
13126                        Bundle.getMessage("ErrorTitle"),
13127                        JmriJOptionPane.ERROR_MESSAGE);
13128                return;
13129            } else {
13130                removeSignalHeadFromPanel(layoutSlip.getSignalC1Name());
13131                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13132                    placeC1Slip(signalHeadName);
13133                } else {
13134                    placeD1Slip(signalHeadName);
13135                }
13136                removeAssignment(c1SlipHead);
13137                layoutSlip.setSignalC1Name(signalHeadName);
13138                needRedraw = true;
13139            }
13140        } else {
13141            LayoutTurnout.Geometry assigned = isHeadAssignedHere(c1SlipHead, layoutSlip);
13142            if (assigned == LayoutTurnout.Geometry.NONE) {
13143                if (isHeadOnPanel(c1SlipHead)
13144                        && isHeadAssignedAnywhere(c1SlipHead)) {
13145                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13146                            Bundle.getMessage("SignalsError8",
13147                                    new Object[]{signalHeadName}),
13148                            Bundle.getMessage("ErrorTitle"),
13149                            JmriJOptionPane.ERROR_MESSAGE);
13150                    return;
13151                } else {
13152                    removeSignalHeadFromPanel(layoutSlip.getSignalC1Name());
13153                    removeAssignment(c1SlipHead);
13154                    layoutSlip.setSignalC1Name(signalHeadName);
13155                }
13156                //} else if (assigned != B1) {
13157                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13158            }
13159        }
13160
13161        if (layoutSlip.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
13162            signalHeadName = c2SlipSignalHeadComboBox.getSelectedItemDisplayName();
13163            if (signalHeadName == null) {
13164                signalHeadName = "";
13165            }
13166            if ((c2SlipHead != null) && setC2SlipHead.isSelected()) {
13167                if (isHeadOnPanel(c2SlipHead)
13168                        && (c2SlipHead != getHeadFromName(layoutSlip.getSignalB2Name()))) {
13169                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13170                            Bundle.getMessage("SignalsError6",
13171                                    new Object[]{signalHeadName}),
13172                            Bundle.getMessage("ErrorTitle"),
13173                            JmriJOptionPane.ERROR_MESSAGE);
13174                    return;
13175                } else {
13176                    removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13177                    if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13178                        placeC2Slip(signalHeadName);
13179                    } else {
13180                        placeD2Slip(signalHeadName);
13181                    }
13182                    removeAssignment(c2SlipHead);
13183                    layoutSlip.setSignalC2Name(signalHeadName);
13184                    needRedraw = true;
13185                }
13186            } else if (c2SlipHead != null) {
13187                LayoutTurnout.Geometry assigned = isHeadAssignedHere(c2SlipHead, layoutSlip);
13188                if (assigned == LayoutTurnout.Geometry.NONE) {
13189                    if (isHeadOnPanel(c2SlipHead)
13190                            && isHeadAssignedAnywhere(c2SlipHead)) {
13191                        JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13192                                Bundle.getMessage("SignalsError8",
13193                                        new Object[]{signalHeadName}),
13194                                Bundle.getMessage("ErrorTitle"),
13195                                JmriJOptionPane.ERROR_MESSAGE);
13196                        return;
13197                    } else {
13198                        removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13199                        removeAssignment(c2SlipHead);
13200                        layoutSlip.setSignalC2Name(signalHeadName);
13201                    }
13202                    //} else if (assigned != B2) {
13203                    //need to figure out what to do in this case - assigned to a different position on the same turnout.
13204                }
13205            } else { //c2SlipHead known to be null here
13206                removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13207                layoutSlip.setSignalC2Name("");
13208            }
13209        } else {
13210            if (c2SlipHead != null) {
13211                BlockBossLogic.getStoppedObject(layoutSlip.getSignalC2Name());
13212                removeSignalHeadFromPanel(layoutSlip.getSignalC2Name());
13213                layoutSlip.setSignalC2Name("");
13214                c2SlipHead = null;
13215            }
13216        }
13217
13218        signalHeadName = d1SlipSignalHeadComboBox.getSelectedItemDisplayName();
13219        if (signalHeadName == null) {
13220            signalHeadName = "";
13221        }
13222        if (setD1SlipHead.isSelected()) {
13223            if (isHeadOnPanel(d1SlipHead)
13224                    && (d1SlipHead != getHeadFromName(layoutSlip.getSignalC1Name()))) {
13225                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13226                        Bundle.getMessage("SignalsError6",
13227                                new Object[]{signalHeadName}),
13228                        Bundle.getMessage("ErrorTitle"),
13229                        JmriJOptionPane.ERROR_MESSAGE);
13230                return;
13231            } else {
13232                removeSignalHeadFromPanel(layoutSlip.getSignalD1Name());
13233                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13234                    placeD1Slip(signalHeadName);
13235                } else {
13236                    placeC1Slip(signalHeadName);
13237                }
13238                removeAssignment(d1SlipHead);
13239                layoutSlip.setSignalD1Name(signalHeadName);
13240                needRedraw = true;
13241            }
13242        } else {
13243            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d1SlipHead, layoutSlip);
13244            if (assigned == LayoutTurnout.Geometry.NONE) {
13245                if (isHeadOnPanel(d1SlipHead)
13246                        && isHeadAssignedAnywhere(d1SlipHead)) {
13247                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13248                            Bundle.getMessage("SignalsError8",
13249                                    new Object[]{signalHeadName}),
13250                            Bundle.getMessage("ErrorTitle"),
13251                            JmriJOptionPane.ERROR_MESSAGE);
13252                    return;
13253                } else {
13254                    removeSignalHeadFromPanel(layoutSlip.getSignalD1Name());
13255                    removeAssignment(d1SlipHead);
13256                    layoutSlip.setSignalD1Name(signalHeadName);
13257                }
13258                //} else if (assigned != C1) {
13259                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13260            }
13261        }
13262
13263        signalHeadName = d2SlipSignalHeadComboBox.getSelectedItemDisplayName();
13264        if (signalHeadName == null) {
13265            signalHeadName = "";
13266        }
13267        if ((d2SlipHead != null) && setD2SlipHead.isSelected()) {
13268            if (isHeadOnPanel(d2SlipHead)
13269                    && (d2SlipHead != getHeadFromName(layoutSlip.getSignalC2Name()))) {
13270                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13271                        Bundle.getMessage("SignalsError6",
13272                                new Object[]{signalHeadName}),
13273                        Bundle.getMessage("ErrorTitle"),
13274                        JmriJOptionPane.ERROR_MESSAGE);
13275                return;
13276            } else {
13277                removeSignalHeadFromPanel(layoutSlip.getSignalD2Name());
13278                if (layoutSlip.getContinuingSense() == Turnout.CLOSED) {
13279                    placeD2Slip(signalHeadName);
13280                } else {
13281                    placeC2Slip(signalHeadName);
13282                }
13283                removeAssignment(d2SlipHead);
13284                layoutSlip.setSignalD2Name(signalHeadName);
13285                needRedraw = true;
13286            }
13287        } else if (d2SlipHead != null) {
13288            LayoutTurnout.Geometry assigned = isHeadAssignedHere(d2SlipHead, layoutSlip);
13289            if (assigned == LayoutTurnout.Geometry.NONE) {
13290                if (isHeadOnPanel(d2SlipHead)
13291                        && isHeadAssignedAnywhere(d2SlipHead)) {
13292                    JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13293                            Bundle.getMessage("SignalsError8",
13294                                    new Object[]{signalHeadName}),
13295                            Bundle.getMessage("ErrorTitle"),
13296                            JmriJOptionPane.ERROR_MESSAGE);
13297                    return;
13298                } else {
13299                    removeSignalHeadFromPanel(layoutSlip.getSignalD2Name());
13300                    removeAssignment(d2SlipHead);
13301                    layoutSlip.setSignalD2Name(signalHeadName);
13302                }
13303                //} else if (assigned != C2) {
13304                //need to figure out what to do in this case - assigned to a different position on the same turnout.
13305            }
13306        } else { //d2SlipHead known to be null here
13307            removeSignalHeadFromPanel(layoutSlip.getSignalD2Name());
13308            layoutSlip.setSignalD2Name("");
13309        }
13310        //setup logic if requested
13311        if (setupA1SlipLogic.isSelected() || setupA2SlipLogic.isSelected()) {
13312            setLogicSlip(a1SlipHead, (TrackSegment) layoutSlip.getConnectC(), a2SlipHead,
13313                    (TrackSegment) layoutSlip.getConnectD(), setupA1SlipLogic.isSelected(),
13314                    setupA2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnout(),
13315                    layoutSlip.getTurnoutB(), LayoutTurnout.STATE_AC, LayoutTurnout.STATE_AD, 0);
13316        }
13317        if (setupB1SlipLogic.isSelected() || setupB2SlipLogic.isSelected()) {
13318            setLogicSlip(b1SlipHead, (TrackSegment) layoutSlip.getConnectD(), b2SlipHead,
13319                    (TrackSegment) layoutSlip.getConnectC(), setupB1SlipLogic.isSelected(),
13320                    setupB2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnout(),
13321                    layoutSlip.getTurnoutB(), LayoutTurnout.STATE_BD, LayoutTurnout.STATE_BC, 2);
13322        }
13323        if (setupC1SlipLogic.isSelected() || setupC2SlipLogic.isSelected()) {
13324            setLogicSlip(c1SlipHead, (TrackSegment) layoutSlip.getConnectA(), c2SlipHead,
13325                    (TrackSegment) layoutSlip.getConnectB(), setupC1SlipLogic.isSelected(),
13326                    setupC2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnoutB(),
13327                    layoutSlip.getTurnout(), LayoutTurnout.STATE_AC, LayoutTurnout.STATE_BC, 4);
13328        }
13329        if (setupD1SlipLogic.isSelected() || setupD2SlipLogic.isSelected()) {
13330            setLogicSlip(d1SlipHead, (TrackSegment) layoutSlip.getConnectB(), d2SlipHead,
13331                    (TrackSegment) layoutSlip.getConnectA(), setupD1SlipLogic.isSelected(),
13332                    setupD2SlipLogic.isSelected(), layoutSlip, layoutSlip.getTurnoutB(),
13333                    layoutSlip.getTurnout(), LayoutTurnout.STATE_BD, LayoutTurnout.STATE_AD, 6);
13334        }
13335        //finish up
13336        setSignalsAtSlipOpenFlag = false;
13337        setSignalsAtSlipFrame.setVisible(false);
13338
13339        if (needRedraw) {
13340            layoutEditor.redrawPanel();
13341            needRedraw = false;
13342            layoutEditor.setDirty();
13343        }
13344    }   //setSlipSignalsDonePressed
13345
13346    private boolean getSlipSignalHeadInformation() {
13347        a1SlipHead = getSignalHeadFromEntry(a1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13348        if (a1SlipHead == null) {
13349            return false;
13350        }
13351        a2SlipHead = getSignalHeadFromEntry(a2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13352
13353        b1SlipHead = getSignalHeadFromEntry(b1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13354        if (b1SlipHead == null) {
13355            return false;
13356        }
13357        b2SlipHead = getSignalHeadFromEntry(b2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13358
13359        c1SlipHead = getSignalHeadFromEntry(c1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13360        if (c1SlipHead == null) {
13361            return false;
13362        }
13363        c2SlipHead = getSignalHeadFromEntry(c2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13364
13365        d1SlipHead = getSignalHeadFromEntry(d1SlipSignalHeadComboBox, true, setSignalsAtSlipFrame);
13366        if (d1SlipHead == null) {
13367            return false;
13368        }
13369        d2SlipHead = getSignalHeadFromEntry(d2SlipSignalHeadComboBox, false, setSignalsAtSlipFrame);
13370
13371        return true;
13372    }
13373
13374    private void placeA1Slip(String signalHeadName) {
13375        //place head near the continuing track of turnout 1
13376        //placingBlock(getSignalHeadIcon(signalHeadName), false, 0.0, layoutSlip.getConnectA(), layoutSlip.getCoordsA());
13377        if (testIcon == null) {
13378            testIcon = signalIconEditor.getIcon(0);
13379        }
13380        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13381
13382        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13383        Point2D coordsA = layoutSlipView.getCoordsA();
13384        Point2D coordsD = layoutSlipView.getCoordsD();
13385        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13386
13387        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
13388        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13389        double diffDirDEG = MathUtil.diffAngleDEG(aDirDEG, dDirDEG);
13390        double shiftX = 0.0;
13391        if (diffDirDEG < 0.0) {
13392            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13393        }
13394        Point2D delta = new Point2D.Double(shiftX, -shift);
13395
13396        delta = MathUtil.rotateDEG(delta, aDirDEG);
13397        Point2D where = MathUtil.add(coordsA, delta);
13398        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
13399    }
13400
13401    private void placeA2Slip(String signalHeadName) {
13402        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13403        //placingBlock(l, false, (4 + l.getHeight()), layoutSlip.getConnectA(), layoutSlip.getCoordsA());
13404        if (testIcon == null) {
13405            testIcon = signalIconEditor.getIcon(0);
13406        }
13407        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13408
13409        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13410        Point2D coordsA = layoutSlipView.getCoordsA();
13411        Point2D coordsD = layoutSlipView.getCoordsD();
13412        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13413
13414        double aDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsA, coordsCenter));
13415        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13416        double diffDirDEG = MathUtil.diffAngleDEG(aDirDEG, dDirDEG);
13417        double shiftX = 2.0 * shift;
13418        if (diffDirDEG < 0.0) {
13419            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13420        }
13421        Point2D delta = new Point2D.Double(shiftX, -shift);
13422
13423        delta = MathUtil.rotateDEG(delta, aDirDEG);
13424        Point2D where = MathUtil.add(coordsA, delta);
13425        setSignalHeadOnPanel(aDirDEG, signalHeadName, where);
13426    }
13427
13428    private void placeB1Slip(String signalHeadName) {
13429        //placingBlock(getSignalHeadIcon(signalHeadName), true, 0.0, layoutSlip.getConnectB(), layoutSlip.getCoordsB());
13430        if (testIcon == null) {
13431            testIcon = signalIconEditor.getIcon(0);
13432        }
13433        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13434
13435        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13436        Point2D coordsB = layoutSlipView.getCoordsB();
13437        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13438
13439        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13440
13441        Point2D delta = new Point2D.Double(+shift, -shift);
13442        delta = MathUtil.rotateDEG(delta, bDirDEG);
13443        Point2D where = MathUtil.add(coordsB, delta);
13444        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
13445    }
13446
13447    private void placeB2Slip(String signalHeadName) {
13448        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13449        //placingBlock(l, true, (4 + l.getHeight()), layoutSlip.getConnectB(), layoutSlip.getCoordsB());
13450        if (testIcon == null) {
13451            testIcon = signalIconEditor.getIcon(0);
13452        }
13453        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13454
13455        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13456        Point2D coordsB = layoutSlipView.getCoordsB();
13457        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13458
13459        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13460
13461        Point2D delta = new Point2D.Double(-shift, -shift);
13462        delta = MathUtil.rotateDEG(delta, bDirDEG);
13463        Point2D where = MathUtil.add(coordsB, delta);
13464        setSignalHeadOnPanel(bDirDEG, signalHeadName, where);
13465    }
13466
13467    private void placeC1Slip(String signalHeadName) {
13468        //placingBlock(getSignalHeadIcon(signalHeadName), false, 0.0, layoutSlip.getConnectC(), layoutSlip.getCoordsC());
13469        if (testIcon == null) {
13470            testIcon = signalIconEditor.getIcon(0);
13471        }
13472        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13473
13474        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13475        Point2D coordsB = layoutSlipView.getCoordsB();
13476        Point2D coordsC = layoutSlipView.getCoordsC();
13477        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13478
13479        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13480        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
13481        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
13482        double shiftX = 0.0;
13483        if (diffDirDEG < 0.0) {
13484            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13485        }
13486        Point2D delta = new Point2D.Double(shiftX, -shift);
13487
13488        delta = MathUtil.rotateDEG(delta, cDirDEG);
13489        Point2D where = MathUtil.add(coordsC, delta);
13490        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
13491    }
13492
13493    private void placeC2Slip(String signalHeadName) {
13494        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13495        //placingBlock(l, false, (4 + l.getHeight()), layoutSlip.getConnectC(), layoutSlip.getCoordsC());
13496        if (testIcon == null) {
13497            testIcon = signalIconEditor.getIcon(0);
13498        }
13499        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13500
13501        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13502        Point2D coordsB = layoutSlipView.getCoordsB();
13503        Point2D coordsC = layoutSlipView.getCoordsC();
13504        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13505
13506        double bDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsB, coordsCenter));
13507        double cDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsC, coordsCenter));
13508        double diffDirDEG = MathUtil.diffAngleDEG(cDirDEG, bDirDEG);
13509        double shiftX = 2.0 * shift;
13510        if (diffDirDEG < 0.0) {
13511            shiftX -= shift * Math.cos(Math.toRadians(diffDirDEG));
13512        }
13513        Point2D delta = new Point2D.Double(shiftX, -shift);
13514
13515        delta = MathUtil.rotateDEG(delta, cDirDEG);
13516        Point2D where = MathUtil.add(coordsC, delta);
13517        setSignalHeadOnPanel(cDirDEG, signalHeadName, where);
13518    }
13519
13520    private void placeD1Slip(String signalHeadName) {
13521        //placingBlock(getSignalHeadIcon(signalHeadName), true, 0.0, layoutSlip.getConnectD(), layoutSlip.getCoordsD());
13522        if (testIcon == null) {
13523            testIcon = signalIconEditor.getIcon(0);
13524        }
13525        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13526
13527        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13528        Point2D coordsD = layoutSlipView.getCoordsD();
13529        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13530
13531        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13532
13533        Point2D delta = new Point2D.Double(+shift, -shift);
13534        delta = MathUtil.rotateDEG(delta, dDirDEG);
13535        Point2D where = MathUtil.add(coordsD, delta);
13536        setSignalHeadOnPanel(dDirDEG, signalHeadName, where);
13537    }
13538
13539    private void placeD2Slip(String signalHeadName) {
13540        //SignalHeadIcon l = getSignalHeadIcon(signalHeadName);
13541        //placingBlock(l, true, (4 + l.getHeight()), layoutSlip.getConnectD(), layoutSlip.getCoordsD());
13542        if (testIcon == null) {
13543            testIcon = signalIconEditor.getIcon(0);
13544        }
13545        double shift = Math.hypot(testIcon.getIconHeight(), testIcon.getIconWidth()) / 2.0;
13546
13547        LayoutSlipView layoutSlipView = layoutEditor.getLayoutSlipView(layoutSlip);
13548        Point2D coordsD = layoutSlipView.getCoordsD();
13549        Point2D coordsCenter = layoutSlipView.getCoordsCenter();
13550
13551        double dDirDEG = MathUtil.wrap360(90.0 - MathUtil.computeAngleDEG(coordsD, coordsCenter));
13552
13553        Point2D delta = new Point2D.Double(-shift, -shift);
13554        delta = MathUtil.rotateDEG(delta, dDirDEG);
13555        Point2D where = MathUtil.add(coordsD, delta);
13556        setSignalHeadOnPanel(dDirDEG, signalHeadName, where);
13557    }
13558
13559    private void setLogicSlip(SignalHead head, TrackSegment track1, SignalHead secondHead, TrackSegment track2,
13560            boolean setup1, boolean setup2,
13561            LayoutSlip slip, Turnout nearTurnout, Turnout farTurnout,
13562            int continueState, int divergeState, int number) {
13563        //initialize common components and ensure all is defined
13564        LayoutBlock connectorBlock = slip.getLayoutBlock();
13565        Sensor connectorOccupancy = null;
13566        if (connectorBlock == null) {
13567            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13568                    Bundle.getMessage("InfoMessage6"),
13569                    Bundle.getMessage("MessageTitle"),
13570                    JmriJOptionPane.INFORMATION_MESSAGE);
13571            return;
13572        }
13573        connectorOccupancy = connectorBlock.getOccupancySensor();
13574        if (connectorOccupancy == null) {
13575            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13576                    Bundle.getMessage("InfoMessage4",
13577                            new Object[]{connectorBlock.getUserName()}),
13578                    Bundle.getMessage("MessageTitle"),
13579                    JmriJOptionPane.INFORMATION_MESSAGE);
13580            return;
13581        }
13582
13583        int nearState = layoutSlip.getTurnoutState(nearTurnout, continueState);
13584        int farState = layoutSlip.getTurnoutState(farTurnout, continueState);
13585
13586        //setup signal head for continuing track of far turnout (or both tracks of far turnout)
13587        if ((track1 == null) && setup1) {
13588            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13589                    Bundle.getMessage("InfoMessage7"),
13590                    Bundle.getMessage("MessageTitle"),
13591                    JmriJOptionPane.INFORMATION_MESSAGE);
13592            return;
13593        }
13594        Sensor occupancy = null;
13595        SignalHead nextHead = null;
13596        if ((track1 != null) && setup1) {
13597            LayoutBlock block = track1.getLayoutBlock();
13598            if (block == null) {
13599                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13600                        Bundle.getMessage("InfoMessage6"),
13601                        Bundle.getMessage("MessageTitle"),
13602                        JmriJOptionPane.INFORMATION_MESSAGE);
13603                return;
13604            }
13605            occupancy = block.getOccupancySensor();
13606            if (occupancy == null) {
13607                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13608                        Bundle.getMessage("InfoMessage4",
13609                                new Object[]{block.getUserName()}),
13610                        Bundle.getMessage("MessageTitle"),
13611                        JmriJOptionPane.INFORMATION_MESSAGE);
13612                return;
13613            }
13614            //need to sort this out???
13615            nextHead = getNextSignalFromObject(track1, slip,
13616                    head.getSystemName(), setSignalsAtSlipFrame);
13617            if ((nextHead == null) && (!reachedEndBumper())) {
13618                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13619                        Bundle.getMessage("InfoMessage5",
13620                                new Object[]{block.getUserName()}),
13621                        Bundle.getMessage("MessageTitle"),
13622                        JmriJOptionPane.INFORMATION_MESSAGE);
13623                return;
13624            }
13625            if (secondHead != null) {
13626                //this head signals only the continuing track of the far turnout
13627                if (!initializeBlockBossLogic(head.getDisplayName())) {
13628                    return;
13629                }
13630                logic.setMode(BlockBossLogic.TRAILINGMAIN);
13631                if (farState == Turnout.THROWN) {
13632                    logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
13633                }
13634                logic.setTurnout(farTurnout.getDisplayName());
13635                logic.setSensor1(occupancy.getDisplayName());
13636                if (occupancy != connectorOccupancy) {
13637                    logic.setSensor2(connectorOccupancy.getDisplayName());
13638                }
13639                if (nextHead != null) {
13640                    logic.setWatchedSignal1(nextHead.getDisplayName(), false);
13641                }
13642                if (auxSignal != null) {
13643                    logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
13644                }
13645                String nearSensorName = setupNearLogixSlip(nearTurnout, nearState, head, farTurnout, farState, slip, number);
13646                addNearSensorToSlipLogic(nearSensorName);
13647                finalizeBlockBossLogic();
13648            }
13649        }
13650        if ((secondHead != null) && !setup2) {
13651            return;
13652        }
13653        SignalHead savedAuxSignal = auxSignal;
13654        if (track2 == null) {
13655            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13656                    Bundle.getMessage("InfoMessage7"),
13657                    Bundle.getMessage("MessageTitle"),
13658                    JmriJOptionPane.INFORMATION_MESSAGE);
13659            return;
13660        }
13661        LayoutBlock block2 = track2.getLayoutBlock();
13662        if (block2 == null) {
13663            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13664                    Bundle.getMessage("InfoMessage6"),
13665                    Bundle.getMessage("MessageTitle"),
13666                    JmriJOptionPane.INFORMATION_MESSAGE);
13667            return;
13668        }
13669        Sensor occupancy2 = block2.getOccupancySensor();
13670        if (occupancy2 == null) {
13671            JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13672                    Bundle.getMessage("InfoMessage4",
13673                            new Object[]{block2.getUserName()}),
13674                    Bundle.getMessage("MessageTitle"),
13675                    JmriJOptionPane.INFORMATION_MESSAGE);
13676            return;
13677        }
13678        SignalHead nextHead2 = null;
13679        if (secondHead != null) {
13680            nextHead2 = getNextSignalFromObject(track2,
13681                    slip, secondHead.getSystemName(), setSignalsAtSlipFrame);
13682            if ((nextHead2 == null) && (!reachedEndBumper())) {
13683                JmriJOptionPane.showMessageDialog(setSignalsAtSlipFrame,
13684                        Bundle.getMessage("InfoMessage5",
13685                                new Object[]{block2.getUserName()}),
13686                        Bundle.getMessage("MessageTitle"),
13687                        JmriJOptionPane.INFORMATION_MESSAGE);
13688                return;
13689            }
13690        }
13691        if ((secondHead == null) && (track1 != null) && setup1) {
13692            if (!initializeBlockBossLogic(head.getDisplayName())) {
13693                return;
13694            }
13695            logic.setMode(BlockBossLogic.FACING);
13696            logic.setTurnout(farTurnout.getDisplayName());
13697            if (occupancy != null) {
13698                logic.setWatchedSensor1(occupancy.getDisplayName());
13699            }
13700            logic.setWatchedSensor2(occupancy2.getDisplayName());
13701            logic.setSensor2(connectorOccupancy.getDisplayName());
13702            if (nextHead != null) {
13703                logic.setWatchedSignal1(nextHead.getDisplayName(), false);
13704            }
13705            if (savedAuxSignal != null) {
13706                logic.setWatchedSignal1Alt(savedAuxSignal.getDisplayName());
13707            }
13708            if (nextHead2 != null) {
13709                logic.setWatchedSignal2(nextHead2.getDisplayName());
13710            }
13711            if (auxSignal != null) {
13712                logic.setWatchedSignal2Alt(auxSignal.getDisplayName());
13713            }
13714            String nearSensorName = setupNearLogixSlip(nearTurnout, nearState, head, farTurnout, farState, slip, number + 1);
13715            addNearSensorToSlipLogic(nearSensorName);
13716            logic.setLimitSpeed2(true);
13717            finalizeBlockBossLogic();
13718        } else if ((secondHead != null) && setup2) {
13719            if (!initializeBlockBossLogic(secondHead.getDisplayName())) {
13720                return;
13721            }
13722            nearState = layoutSlip.getTurnoutState(nearTurnout, divergeState);
13723            farState = layoutSlip.getTurnoutState(farTurnout, divergeState);
13724
13725            logic.setMode(BlockBossLogic.TRAILINGDIVERGING);
13726            if (farState == Turnout.CLOSED) {
13727                logic.setMode(BlockBossLogic.TRAILINGMAIN);
13728                logic.setLimitSpeed1(true);
13729            } else {
13730                logic.setLimitSpeed2(true);
13731            }
13732            logic.setTurnout(farTurnout.getDisplayName());
13733            logic.setSensor1(occupancy2.getDisplayName());
13734            if (occupancy2 != connectorOccupancy) {
13735                logic.setSensor2(connectorOccupancy.getDisplayName());
13736            }
13737            if (nextHead2 != null) {
13738                logic.setWatchedSignal1(nextHead2.getDisplayName(), false);
13739            }
13740            if (auxSignal != null) {
13741                logic.setWatchedSignal1Alt(auxSignal.getDisplayName());
13742            }
13743            String nearSensorName = setupNearLogixSlip(nearTurnout, nearState, secondHead, farTurnout, farState, slip, number + 1);
13744            addNearSensorToSlipLogic(nearSensorName);
13745            finalizeBlockBossLogic();
13746        }
13747    }   //setLogicSlip
13748
13749    private String setupNearLogixSlip(Turnout turn, int nearState,
13750            SignalHead head, Turnout farTurn, int farState, LayoutSlip slip, int number) {
13751        String turnoutName = turn.getDisplayName();
13752        String farTurnoutName = farTurn.getDisplayName();
13753
13754        String logixPrefix = InstanceManager.getDefault(jmri.LogixManager.class).getSystemNamePrefix();
13755        String pref = InstanceManager.getDefault(jmri.LogixManager.class).getSystemPrefix();
13756        String logixName = logixPrefix + ":IX_LAYOUTSLIP:" + slip.getId();
13757        String sensorName = pref + "S:" + logixName + "C" + number;
13758        try {
13759            InstanceManager.sensorManagerInstance().provideSensor(sensorName);
13760        } catch (IllegalArgumentException ex) {
13761            log.error("Trouble creating sensor {} while setting up Logix.", sensorName);
13762            return "";
13763        }
13764        boolean newConditional = false;
13765        Logix x = InstanceManager.getDefault(LogixManager.class
13766        ).getBySystemName(logixName);
13767
13768        if (x == null) {
13769            x = InstanceManager.getDefault(LogixManager.class
13770            ).createNewLogix(logixName, "");
13771            newConditional = true;
13772            if (x == null) {
13773                log.error("Trouble creating logix {} while setting up signal logic.", logixName);
13774                return "";
13775            }
13776            x.setComment("Layout Slip, Signalhead logic");
13777        }
13778        x.deActivateLogix();
13779        String cName = logixName + "C" + number;
13780
13781        Conditional c = InstanceManager.getDefault(ConditionalManager.class
13782        ).getBySystemName(cName);
13783
13784        if (c == null) {
13785            c = InstanceManager.getDefault(ConditionalManager.class
13786            ).
13787                    createNewConditional(cName, "");
13788            newConditional = true;
13789            if (c == null) {
13790                log.error("Trouble creating conditional {} while setting up Logix.", cName);
13791                return "";
13792            }
13793        }
13794        Conditional.Type type = Conditional.Type.TURNOUT_THROWN;
13795        if (nearState == Turnout.CLOSED) {
13796            type = Conditional.Type.TURNOUT_CLOSED;
13797        }
13798        ArrayList<ConditionalVariable> variableList = new ArrayList<>();
13799        variableList.add(new ConditionalVariable(false, Conditional.Operator.AND,
13800                type, turnoutName, true));
13801
13802        type = Conditional.Type.TURNOUT_THROWN;
13803        if (farState == Turnout.CLOSED) {
13804            type = Conditional.Type.TURNOUT_CLOSED;
13805        }
13806        variableList.add(new ConditionalVariable(false, Conditional.Operator.AND,
13807                type, farTurnoutName, true));
13808        c.setStateVariables(variableList);
13809        ArrayList<ConditionalAction> actionList = new ArrayList<>();
13810        actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
13811                Conditional.Action.SET_SENSOR, sensorName,
13812                Sensor.INACTIVE, ""));
13813        actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE,
13814                Conditional.Action.SET_SENSOR, sensorName,
13815                Sensor.ACTIVE, ""));
13816        c.setAction(actionList); //string data
13817        if (newConditional) {
13818            x.addConditional(cName, -1);
13819        }
13820        x.activateLogix();
13821        return sensorName;
13822    }   //setupNearLogixSlip
13823
13824    /*
13825     * Adds the sensor specified to the open BlockBossLogic, provided it is not already there and
13826     * provided there is an open slot. If 'name' is null or empty, returns without doing anything.
13827     */
13828    private void addNearSensorToSlipLogic(String name) {
13829        if ((name != null) && !name.isEmpty()) {
13830            //return if a sensor by this name is already present
13831            if (logic.getSensor1() != null && logic.getSensor1().equals(name)) {
13832                return;
13833            }
13834            if (logic.getSensor2() != null && logic.getSensor2().equals(name)) {
13835                return;
13836            }
13837            if (logic.getSensor3() != null && logic.getSensor3().equals(name)) {
13838                return;
13839            }
13840            if (logic.getSensor4() != null && logic.getSensor4().equals(name)) {
13841                return;
13842            }
13843            if (logic.getSensor5() != null && logic.getSensor5().equals(name)) {
13844                return;
13845            }
13846            //add in the first available slot
13847            if (logic.getSensor1() == null) {
13848                logic.setSensor1(name);
13849            } else if (logic.getSensor2() == null) {
13850                logic.setSensor2(name);
13851            } else if (logic.getSensor3() == null) {
13852                logic.setSensor3(name);
13853            } else if (logic.getSensor4() == null) {
13854                logic.setSensor4(name);
13855            } else if (logic.getSensor5() == null) {
13856                logic.setSensor5(name);
13857            } else {
13858                log.error("Error - could not add sensor to SSL for signal head {}", logic.getDrivenSignal());
13859            }
13860        }
13861    }
13862
13863    /**
13864     * get a signal head icon for the given signal head
13865     *
13866     * @param signalName name of a signal head.
13867     * @return a SignalHeadIcon for the signal.
13868     */
13869    @CheckReturnValue
13870    public SignalHeadIcon getSignalHeadIcon(@Nonnull String signalName) {
13871        if (signalIconEditor == null) {
13872            signalIconEditor = layoutEditor.getLayoutEditorToolBarPanel().signalIconEditor;
13873        }
13874        SignalHeadIcon l = new SignalHeadIcon(layoutEditor);
13875        l.setSignalHead(signalName);
13876        l.setIcon("SignalHeadStateRed", signalIconEditor.getIcon(0));
13877        l.setIcon("SignalHeadStateFlashingRed", signalIconEditor.getIcon(1));
13878        l.setIcon("SignalHeadStateYellow", signalIconEditor.getIcon(2));
13879        l.setIcon("SignalHeadStateFlashingYellow", signalIconEditor.getIcon(3));
13880        l.setIcon("SignalHeadStateGreen", signalIconEditor.getIcon(4));
13881        l.setIcon("SignalHeadStateFlashingGreen", signalIconEditor.getIcon(5));
13882        l.setIcon("SignalHeadStateDark", signalIconEditor.getIcon(6));
13883        l.setIcon("SignalHeadStateHeld", signalIconEditor.getIcon(7));
13884        l.setIcon("SignalHeadStateLunar", signalIconEditor.getIcon(8));
13885        l.setIcon("SignalHeadStateFlashingLunar", signalIconEditor.getIcon(9));
13886        l.rotate(90);
13887        return l;
13888    }
13889
13890    //convenience strings
13891    private final String eastString = Bundle.getMessage("East");
13892    private final String westString = Bundle.getMessage("West");
13893    private final String continuingString = Bundle.getMessage("Continuing");
13894    private final String divergingString = Bundle.getMessage("Diverging");
13895    private final String throatString = Bundle.getMessage("Throat");
13896    private final String throatContinuingString = Bundle.getMessage("ThroatContinuing");
13897    private final String throatDivergingString = Bundle.getMessage("ThroatDiverging");
13898
13899    private final String divergingAString = Bundle.getMessage("Diverging_", "A");
13900    private final String divergingBString = Bundle.getMessage("Diverging_", "B");
13901
13902    protected Boolean addLayoutTurnoutSignalHeadInfoToMenu(
13903            @Nonnull String inTurnoutNameA, @Nonnull String inTurnoutNameB,
13904            @Nonnull JMenu inMenu) {
13905        Boolean result = false; //assume failure (pessimist!)
13906
13907        //lookup turnouts
13908        turnout = turnout1 = turnoutA = InstanceManager.turnoutManagerInstance().getTurnout(inTurnoutNameA);
13909        turnout2 = turnoutB = InstanceManager.turnoutManagerInstance().getTurnout(inTurnoutNameB);
13910        //map those to layout turnouts (if possible)
13911        for (LayoutTurnout lt : layoutEditor.getLayoutTurnouts()) {
13912            Turnout to = lt.getTurnout();
13913            if (to != null) {
13914                String uname = to.getUserName();
13915                String sname = to.getSystemName();
13916                if (!inTurnoutNameA.isEmpty() && (sname.equals(inTurnoutNameA) || ((uname != null) && uname.equals(inTurnoutNameA)))) {
13917                    layoutTurnout = layoutTurnout1 = layoutTurnoutA = lt;
13918                }
13919                if (!inTurnoutNameB.isEmpty() && (sname.equals(inTurnoutNameB) || ((uname != null) && uname.equals(inTurnoutNameB)))) {
13920                    layoutTurnout2 = layoutTurnoutB = lt;
13921                }
13922            }
13923        }
13924
13925        int before_mcc = inMenu.getMenuComponentCount();
13926        if (before_mcc != 0) {
13927            inMenu.add(new JSeparator());
13928        }
13929        LayoutTurnout.LinkType linkType = layoutTurnout.getLinkType();
13930        if ((layoutTurnout.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
13931                || (layoutTurnout.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)
13932                || (layoutTurnout.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)) {
13933            JMenuItem jmi = inMenu.add(Bundle.getMessage("Crossover"));
13934            jmi.setEnabled(false);
13935            inMenu.add(new JSeparator());
13936            before_mcc += 2;
13937            addInfoToMenu("A " + continuingString, layoutTurnout.getSignalA1Name(), inMenu);
13938            addInfoToMenu("A " + divergingString, layoutTurnout.getSignalA2Name(), inMenu);
13939            addInfoToMenu("B " + continuingString, layoutTurnout.getSignalB1Name(), inMenu);
13940            addInfoToMenu("B " + divergingString, layoutTurnout.getSignalB2Name(), inMenu);
13941            addInfoToMenu("C " + continuingString, layoutTurnout.getSignalC1Name(), inMenu);
13942            addInfoToMenu("C " + divergingString, layoutTurnout.getSignalC2Name(), inMenu);
13943            addInfoToMenu("D " + continuingString, layoutTurnout.getSignalD1Name(), inMenu);
13944            addInfoToMenu("D " + divergingString, layoutTurnout.getSignalD2Name(), inMenu);
13945        } else if (linkType == LayoutTurnout.LinkType.NO_LINK) {
13946            JMenuItem jmi = inMenu.add(Bundle.getMessage("BeanNameTurnout"));
13947            jmi.setEnabled(false);
13948            inMenu.add(new JSeparator());
13949            before_mcc += 2;
13950            addInfoToMenu(throatContinuingString, layoutTurnout.getSignalA1Name(), inMenu);
13951            addInfoToMenu(throatDivergingString, layoutTurnout.getSignalA2Name(), inMenu);
13952            addInfoToMenu(continuingString, layoutTurnout.getSignalB1Name(), inMenu);
13953            addInfoToMenu(divergingString, layoutTurnout.getSignalC1Name(), inMenu);
13954        } else if (linkType == LayoutTurnout.LinkType.THROAT_TO_THROAT) {
13955            String menuString = Bundle.getMessage("ThroatToThroat") + " (";
13956            menuString += Bundle.getMessage("BeanNameTurnout") + ", " + Bundle.getMessage("BeanNameRoute");
13957            menuString += ", " + Bundle.getMessage("BeanNameSignalHead") + ":)";
13958            JMenuItem jmi = inMenu.add(menuString);
13959            jmi.setEnabled(false);
13960            inMenu.add(new JSeparator());
13961            before_mcc += 2;
13962            addInfoToMenu(eastString + ", " + continuingString + ", " + continuingString, layoutTurnout1.getSignalB1Name(), inMenu);
13963            addInfoToMenu(eastString + ", " + continuingString + ", " + divergingString, layoutTurnout1.getSignalB2Name(), inMenu);
13964            addInfoToMenu(eastString + ", " + divergingString + ", " + continuingString, layoutTurnout1.getSignalC1Name(), inMenu);
13965            addInfoToMenu(eastString + ", " + divergingString + ", " + divergingString, layoutTurnout1.getSignalC2Name(), inMenu);
13966            addInfoToMenu(westString + ", " + continuingString + ", " + continuingString, layoutTurnout2.getSignalB1Name(), inMenu);
13967            addInfoToMenu(westString + ", " + continuingString + ", " + divergingString, layoutTurnout2.getSignalB2Name(), inMenu);
13968            addInfoToMenu(westString + ", " + divergingString + ", " + continuingString, layoutTurnout2.getSignalC1Name(), inMenu);
13969            addInfoToMenu(westString + ", " + divergingString + ", " + divergingString, layoutTurnout2.getSignalC2Name(), inMenu);
13970        } else if (linkType == LayoutTurnout.LinkType.FIRST_3_WAY) {
13971            JMenuItem jmi = inMenu.add(Bundle.getMessage("ThreeWay"));
13972            jmi.setEnabled(false);
13973            inMenu.add(new JSeparator());
13974            before_mcc += 2;
13975            addInfoToMenu(throatString + " " + continuingString, layoutTurnoutA.getSignalA1Name(), inMenu);
13976            addInfoToMenu(throatString + " " + divergingAString, layoutTurnoutA.getSignalA2Name(), inMenu);
13977            addInfoToMenu(throatString + " " + divergingBString, layoutTurnoutA.getSignalA3Name(), inMenu);
13978            addInfoToMenu(continuingString, layoutTurnoutA.getSignalC1Name(), inMenu);
13979            addInfoToMenu(divergingAString, layoutTurnoutB.getSignalB1Name(), inMenu);
13980            addInfoToMenu(divergingBString, layoutTurnoutB.getSignalC1Name(), inMenu);
13981        } else if (linkType == LayoutTurnout.LinkType.SECOND_3_WAY) {
13982            JMenuItem jmi = inMenu.add(Bundle.getMessage("ThreeWay"));
13983            jmi.setEnabled(false);
13984            inMenu.add(new JSeparator());
13985            before_mcc += 2;
13986            addInfoToMenu(throatString + " " + continuingString, layoutTurnoutB.getSignalA1Name(), inMenu);
13987            addInfoToMenu(throatString + " " + divergingAString, layoutTurnoutB.getSignalA2Name(), inMenu);
13988            addInfoToMenu(throatString + " " + divergingBString, layoutTurnoutB.getSignalA3Name(), inMenu);
13989            addInfoToMenu(continuingString, layoutTurnoutB.getSignalC1Name(), inMenu);
13990            addInfoToMenu(divergingAString, layoutTurnoutA.getSignalB1Name(), inMenu);
13991            addInfoToMenu(divergingBString, layoutTurnoutA.getSignalC1Name(), inMenu);
13992        }
13993        int after_mcc = inMenu.getMenuComponentCount();
13994        if (before_mcc != after_mcc) {
13995            inMenu.add(new JSeparator());
13996            result = true;   //it's GOOD!
13997        }
13998        return result;
13999    }   //addLayoutTurnoutSignalHeadInfoToMenu
14000
14001    protected Boolean addBlockBoundarySignalHeadInfoToMenu(
14002            @Nonnull PositionablePoint inPositionablePoint,
14003            @Nonnull JMenu inMenu) {
14004        Boolean result = false; //assume failure (pessimist!)
14005
14006        int before_mcc = inMenu.getMenuComponentCount();
14007        if (before_mcc != 0) {
14008            inMenu.add(new JSeparator());
14009        }
14010
14011        JMenuItem jmi = inMenu.add(Bundle.getMessage("BlockBoundary"));
14012        jmi.setEnabled(false);
14013        inMenu.add(new JSeparator());
14014        before_mcc += 2;
14015
14016        addInfoToMenu(Bundle.getMessage("East/SouthBound"), inPositionablePoint.getEastBoundSignal(), inMenu);
14017        addInfoToMenu(Bundle.getMessage("West/NorthBound"), inPositionablePoint.getWestBoundSignal(), inMenu);
14018
14019        int after_mcc = inMenu.getMenuComponentCount();
14020        if (before_mcc != after_mcc) {
14021            inMenu.add(new JSeparator());
14022            result = true;   //it's GOOD!
14023        }
14024
14025        return result;
14026    }
14027
14028    protected Boolean addLevelXingSignalHeadInfoToMenu(
14029            @Nonnull LevelXing inLevelXing,
14030            @Nonnull JMenu inMenu) {
14031        Boolean result = false; //assume failure (pessimist!)
14032
14033        int before_mcc = inMenu.getMenuComponentCount();
14034        if (before_mcc != 0) {
14035            inMenu.add(new JSeparator());
14036        }
14037
14038        JMenuItem jmi = inMenu.add(Bundle.getMessage("LevelCrossing"));
14039        jmi.setEnabled(false);
14040        inMenu.add(new JSeparator());
14041        before_mcc += 2;
14042
14043        addInfoToMenu(Bundle.getMessage("MakeLabel",
14044                Bundle.getMessage("TrackXConnect", "A")),
14045                inLevelXing.getSignalAName(), inMenu);
14046        addInfoToMenu(Bundle.getMessage("MakeLabel",
14047                Bundle.getMessage("TrackXConnect", "B")),
14048                inLevelXing.getSignalBName(), inMenu);
14049        addInfoToMenu(Bundle.getMessage("MakeLabel",
14050                Bundle.getMessage("TrackXConnect", "C")),
14051                inLevelXing.getSignalCName(), inMenu);
14052        addInfoToMenu(Bundle.getMessage("MakeLabel",
14053                Bundle.getMessage("TrackXConnect", "D")),
14054                inLevelXing.getSignalDName(), inMenu);
14055
14056        int after_mcc = inMenu.getMenuComponentCount();
14057        if (before_mcc != after_mcc) {
14058            inMenu.add(new JSeparator());
14059            result = true;   //it's GOOD!
14060        }
14061
14062        return result;
14063    }
14064
14065    protected Boolean addLayoutSlipSignalHeadInfoToMenu(
14066            @Nonnull LayoutTurnout inLayoutTurnout,
14067            @Nonnull JMenu inMenu) {
14068        Boolean result = false; //assume failure (pessimist!)
14069
14070        int before_mcc = inMenu.getMenuComponentCount();
14071        if (before_mcc != 0) {
14072            inMenu.add(new JSeparator());
14073        }
14074
14075        JMenuItem jmi = inMenu.add(Bundle.getMessage("Slip"));
14076        jmi.setEnabled(false);
14077        inMenu.add(new JSeparator());
14078        before_mcc += 2;
14079
14080        addInfoToMenu("A " + continuingString, inLayoutTurnout.getSignalA1Name(), inMenu);
14081        addInfoToMenu("A " + divergingString, inLayoutTurnout.getSignalA2Name(), inMenu);
14082        addInfoToMenu("B " + continuingString, inLayoutTurnout.getSignalB1Name(), inMenu);
14083        addInfoToMenu("B " + divergingString, inLayoutTurnout.getSignalB2Name(), inMenu);
14084        addInfoToMenu("C " + continuingString, inLayoutTurnout.getSignalC1Name(), inMenu);
14085        addInfoToMenu("C " + divergingString, inLayoutTurnout.getSignalC2Name(), inMenu);
14086        addInfoToMenu("D " + continuingString, inLayoutTurnout.getSignalD1Name(), inMenu);
14087        addInfoToMenu("D " + divergingString, inLayoutTurnout.getSignalD2Name(), inMenu);
14088
14089        int after_mcc = inMenu.getMenuComponentCount();
14090        if (before_mcc != after_mcc) {
14091            inMenu.add(new JSeparator());
14092            result = true;   //it's GOOD!
14093        }
14094
14095        return result;
14096    }
14097
14098    private void addInfoToMenu(@CheckForNull String title,
14099            @CheckForNull String info, @Nonnull JMenu menu) {
14100        if ((title != null) && !title.isEmpty() && (info != null) && !info.isEmpty()) {
14101            addInfoToMenu(title + ": " + info, menu);
14102        }
14103    }
14104
14105    private void addInfoToMenu(@CheckForNull String info, @Nonnull JMenu menu) {
14106        if ((info != null) && !info.isEmpty()) {
14107            JMenuItem jmi = new JMenuItem(info);
14108            jmi.setEnabled(false);
14109            menu.add(jmi);
14110        }
14111    }
14112
14113    private void oneFrameToRuleThemAll(@Nonnull JmriJFrame goodFrame) {
14114        setSensorsAtBlockBoundaryFrame = closeIfNotFrame(goodFrame, setSensorsAtBlockBoundaryFrame);
14115        setSensorsAtLevelXingFrame = closeIfNotFrame(goodFrame, setSensorsAtLevelXingFrame);
14116        setSensorsAtSlipFrame = closeIfNotFrame(goodFrame, setSensorsAtSlipFrame);
14117        setSensorsAtTurnoutFrame = closeIfNotFrame(goodFrame, setSensorsAtTurnoutFrame);
14118        setSignalMastsAtBlockBoundaryFrame = closeIfNotFrame(goodFrame, setSignalMastsAtBlockBoundaryFrame);
14119        setSignalMastsAtLayoutSlipFrame = closeIfNotFrame(goodFrame, setSignalMastsAtLayoutSlipFrame);
14120        setSignalMastsAtLevelXingFrame = closeIfNotFrame(goodFrame, setSignalMastsAtLevelXingFrame);
14121        setSignalMastsAtTurnoutFrame = closeIfNotFrame(goodFrame, setSignalMastsAtTurnoutFrame);
14122        setSignalsAt3WayTurnoutFrame = closeIfNotFrame(goodFrame, setSignalsAt3WayTurnoutFrame);
14123        setSignalsAtBlockBoundaryFrame = closeIfNotFrame(goodFrame, setSignalsAtBlockBoundaryFrame);
14124        setSignalsAtLevelXingFrame = closeIfNotFrame(goodFrame, setSignalsAtLevelXingFrame);
14125        setSignalsAtSlipFrame = closeIfNotFrame(goodFrame, setSignalsAtSlipFrame);
14126        setSignalsAtThroatToThroatTurnoutsFrame = closeIfNotFrame(goodFrame, setSignalsAtThroatToThroatTurnoutsFrame);
14127        setSignalsAtTurnoutFrame = closeIfNotFrame(goodFrame, setSignalsAtTurnoutFrame);
14128        setSignalsAtXoverTurnoutFrame = closeIfNotFrame(goodFrame, setSignalsAtXoverTurnoutFrame);
14129    }
14130
14131    private JmriJFrame closeIfNotFrame(@Nonnull JmriJFrame goodFrame, @CheckForNull JmriJFrame badFrame) {
14132        JmriJFrame result = badFrame;
14133        if ((badFrame != null) && (goodFrame != badFrame)) {
14134            badFrame.setVisible(false);
14135            badFrame.dispose();
14136            result = null;
14137        }
14138        return result;
14139    }
14140
14141    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutEditorTools.class);
14142}