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