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                    divergingBString)));
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    Set<SignalMast> usedMasts = new HashSet<>();
9641
9642    void createListUsedSignalMasts() {
9643        usedMasts = new HashSet<>();
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    }   //createListUsedSignalMasts
9685
9686    void refreshSignalMastAtTurnoutComboBox() {
9687        turnoutSignalMastsGetSaved(null);
9688